This example demonstrates how to use RTK Query with Vite and TypeScript to fetch data in a modern React app.
To create a new project using the official Vite + Redux Toolkit + TypeScript template:
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
cd my-app
npm install
This sets up a modern React + Redux + TypeScript app with Vite and includes testing tools like Vitest.
π Redux Toolkit Getting Started
Absolutely! Here's a section you can add to your README that provides alternative setup instructions for a Vite + React + RTK Query project using plain JavaScript (no TypeScript).
If you prefer to use JavaScript instead of TypeScript, you can set up a Vite + React + Redux Toolkit project manually with the following steps:
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm install @reduxjs/toolkit react-redux
Create a file at src/app/store.js
:
import { configureStore } from '@reduxjs/toolkit';
import { swapiApi } from '../features/swapi/swapiApi';
export const store = configureStore({
reducer: {
[swapiApi.reducerPath]: swapiApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(swapiApi.middleware),
});
Create a file at src/features/swapi/swapiApi.js
:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const swapiApi = createApi({
reducerPath: 'swapiApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://swapi.dev/api/' }),
endpoints: (builder) => ({
getPeople: builder.query({
query: () => 'people',
}),
}),
});
export const { useGetPeopleQuery } = swapiApi;
Update src/main.jsx
:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './app/store';
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
);
In src/App.jsx
:
import { useGetPeopleQuery } from './features/swapi/swapiApi';
function App() {
const { data, error, isLoading } = useGetPeopleQuery();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading data.</div>;
return (
<div>
<h1>Star Wars Characters</h1>
<ul>
{data.results.map((character) => (
<li key={character.name}>{character.name}</li>
))}
</ul>
</div>
);
}
export default App;
This setup avoids TypeScript and sticks to JavaScript, making it ideal for beginner-friendly projects or faster prototyping.
To use RTK Query:
- Create a new slice file (e.g.,
features/swapi/swapiApi.ts
) and define your API:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const swapiApi = createApi({
reducerPath: 'swapiApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://swapi.dev/api/' }),
endpoints: (builder) => ({
getPeople: builder.query({
query: () => 'people',
}),
}),
});
export const { useGetPeopleQuery } = swapiApi;
- Add the API to your store (
app/store.ts
):
import { configureStore } from '@reduxjs/toolkit';
import { swapiApi } from '../features/swapi/swapiApi';
export const store = configureStore({
reducer: {
[swapiApi.reducerPath]: swapiApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(swapiApi.middleware),
});
- Wrap your app in the Redux
<Provider>
(main.tsx
):
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { store } from './app/store';
import { Provider } from 'react-redux';
ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<App />
</Provider>
);
The project in this repository includes a few examples using RTK Query, including:
- Loads data when the component renders.
- Uses the hook generated by
createApi
.
- Loads data on demand using a button.
- Uses
useLazy...Query
to manually trigger the request.
π RTK Query Overview
-
Modify SWAPI Components Display additional fields like height, birth year, or gender.
-
Create a Weather Query Use the OpenWeatherMap API and follow the structure of the SWAPI example to create your own weather data component.
-
Study the Examples Look at
features/quotes
orfeatures/swapi
. -
Create a New Folder Add a new folder inside
features/
for your API (e.g.,features/weather/
). -
Add Two Files
weatherApi.ts
: YourcreateApi
slice.WeatherComponent.tsx
: A React component using the generated hook.
-
Define the API Slice Use
createApi
with:baseQuery
reducerPath
tagTypes
(optional)endpoints
that returnbuilder.query()
orbuilder.mutation()
-
Export the Hook Hooks are auto-generated from your endpoint name:
getBagels
βuseGetBagelsQuery
-
Add to the Store Import your slice into
store.ts
:- Add its reducer to
configureStore
. - Add its middleware using
.concat()
.
- Add its reducer to
-
Use the Hook in Your Component
- Call the hook:
useGetBagelsQuery()
- Handle
isLoading
,isError
, anddata
- Display the result
- Call the hook:
π§ββοΈ Magic Explained: RTK Query auto-generates hooks from your endpoint names, saving you boilerplate code and ensuring consistency.
This project uses vite-template-redux
:
- β‘ Vite for fast builds and dev server
- βοΈ React + Redux Toolkit + TypeScript
- π¬ Vitest and React Testing Library
npm run dev # Start the dev server
npm run build # Build the app for production
npm run preview # Preview the built app locally
npm run test # Run tests
# Redux Tool Kit Query
This example started with Create React with the Redux Toolkit template.
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
https://redux-toolkit.js.org/introduction/getting-started
This template uses Vite. So starting and running the app along with directory structure is a little a different. The project also uses type script.
This example uses RTK Query which sets up RTK to load async data into the store. I added the SWAPI component which uses RTK Query to load data from https://swapi.dev. I commented the code in those files to hopefully explain what is going on.
https://redux-toolkit.js.org/rtk-query/overview
There are two examples: SWAPI and SWAPI2.
The SWAPI example uses a query to load data. In this example the query is executed when the component is RENDERED! Notice that data is loaded when the component mounts, and when state updates.
The SWAPI2 example uses a lzy query. This query runs when the trigger is executed. In this example data is not loaded until the button is clicked.
Compare these two files and test both components to get an idea of how these are working and what the differences are!
## Challenges!
1. Modify the components presented here. The SWAPI components just display, eye color, and mass. Display a few more features as an easy challenge.
2. Make a new query api using openweathermap. Use the components here as a guide. Follow the steps in the following section.
## Try it yourself!
Try it youself by following these steps:
1. Take a look at features/quotes or features/swapi you'll recreate one of these with a different web api.
2. Create a new folfer in features for your new slice.
3. Add two new files, one for a component and another for the slice.
4. In your slice, import createApi and fetchQuery.
5. Use creatApi to define the new slice. Follow one of the examples. and set up the baseQuery, reducerPath, tagTypes, and enpoints. Note! The get... endpoint will be the hook! The query takes any parameters that need to be inlcuded in the query. Quotes uses query vars and SWAPI uses params. Consult one or the other depending on your Web API.
6. Export the hook by making the name from the get... query you wrote and adding use...Query. For example getBagels becomes: useGetBagelsQuery -> use...GetBagels...Query. It's magic!
7. Now add this slice to your store. Open the store and import your your slice. Find the rootReducer and add your slice to combineReducers. This seems to make the actions and reducers like magic!
8. In configureStore add your slice to the middleware array. Look at the example to see how this was done. NOTE! If you only have one slice you only need to concat once!
9. Now make your component! Start by importing your api hook, useGetBagelsQuery in the example. Call it in react-query style. See the example. You'll need to pass any parameters required by your query!
10. Handle isError, isLoading, and isSuccess, then use data to display the data loaded. This is a good spot to check and debug if the slice is error free.
# vite-template-redux
Uses [Vite](https://vitejs.dev/), [Vitest](https://vitest.dev/), and [React Testing Library](https://github.com/testing-library/react-testing-library) to create a modern [React](https://react.dev/) app compatible with [Create React App](https://create-react-app.dev/)
```sh
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
- Easy migration from Create React App or Vite
- As beginner friendly as Create React App
- Optimized performance compared to Create React App
- Customizable without ejecting
dev
/start
- start dev server and open browserbuild
- build for productionpreview
- locally preview production buildtest
- launch test runner