Building a Modern React App with Vite, Redux Toolkit, MUI, and TypeScript
Build a robust React application with Redux Toolkit, Material-UI, and TypeScript using Vite: Step-by-step guide with state management and data persist
In this article, we will walk through the creation of a modern React application using Vite, Redux Toolkit, Material-UI (MUI), and TypeScript. Our app will include a registration page and a page to display registered users. We will use Redux Toolkit for state management and persist data in the browser.
Why Vite, Redux Toolkit, MUI, and TypeScript?
Vite: A build tool that offers fast development server start times and instant hot module replacement.
Redux Toolkit: A set of tools to simplify the setup and use of Redux for state management.
Material-UI (MUI): A popular React UI framework with a set of pre-styled components.
TypeScript: Provides static typing, which helps to catch errors early and improve code quality.
Project Setup
First, let's create a new project using Vite.
Step 1: Create a Vite Project
npm create vite@latest vite-redux-toolkit-mui-starter --template react-ts
cd vite-redux-toolkit-mui-starter
npm install
Step 2: Install Required Dependencies
npm install @reduxjs/toolkit react-redux @mui/material @emotion/react @emotion/styled redux-persist
Project Structure
Here's an overview of the project structure:
Building the App
Step 1: Setting up Redux Store
Create a store.ts
file in the src/store
directory:
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
users: userReducer,
});
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
reducer: persistedReducer,
});
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
Step 2: Creating User Slice
Create a userSlice.ts
file in the src/store
directory:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface User {
id: string;
name: string;
email: string;
}
interface UsersState {
users: User[];
}
const initialState: UsersState = {
users: [],
};
const userSlice = createSlice({
name: 'users',
initialState,
reducers: {
addUser: (state, action: PayloadAction<User>) => {
state.users.push(action.payload);
},
},
});
export const { addUser } = userSlice.actions;
export default userSlice.reducer;
Step 3: Creating Register Page
Create a RegisterPage.tsx
file in the src/pages
directory:
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addUser } from '../store/userSlice';
import { Box, Button, TextField, Typography } from '@mui/material';
import { v4 as uuidv4 } from 'uuid';
const RegisterPage: React.FC = () => {
const dispatch = useDispatch();
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
dispatch(addUser({ id: uuidv4(), name, email }));
setName('');
setEmail('');
};
return (
<Box>
<Typography variant="h4">Register</Typography>
<form onSubmit={handleSubmit}>
<TextField
label="Name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<TextField
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Button type="submit" variant="contained" color="primary">
Register
</Button>
</form>
</Box>
);
};
export default RegisterPage;
Step 4: Creating Registered Users Page
Create a RegisteredUsersPage.tsx
file in the src/pages
directory:
import React from 'react';
import { useSelector } from 'react-redux';
import { RootState } from '../store/store';
import { Box, Typography, List, ListItem } from '@mui/material';
const RegisteredUsersPage: React.FC = () => {
const users = useSelector((state: RootState) => state.users.users);
return (
<Box>
<Typography variant="h4">Registered Users</Typography>
<List>
{users.map((user) => (
<ListItem key={user.id}>
{user.name} - {user.email}
</ListItem>
))}
</List>
</Box>
);
};
export default RegisteredUsersPage;
Step 5: Updating App Component
Update the App.tsx
file to include routing and the new pages:
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
import { AppBar, Toolbar, Typography, Button, Container } from '@mui/material';
import HomePage from './pages/HomePage';
import RegisterPage from './pages/RegisterPage';
import RegisteredUsersPage from './pages/RegisteredUsersPage';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import store, { persistor } from './store/store';
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Router>
<AppBar position="static" style={{ minWidth: '100vw', background: 'green' }}>
<Toolbar style={{ display: 'flex', justifyContent: 'space-between' }}>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Redux Toolkit
</Typography>
<Button color="inherit" component={Link} to="/">Home</Button>
<Button color="inherit" component={Link} to="/register">Register</Button>
<Button color="inherit" component={Link} to="/users">Users</Button>
</Toolbar>
</AppBar>
<Container>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/users" element={<RegisteredUsersPage />} />
</Routes>
</Container>
</Router>
</PersistGate>
</Provider>
);
};
export default App;
Step 6: Adding Header and Footer Components
Create Header.tsx
and Footer.tsx
files in the src/components
directory:
Header.tsx
import React from 'react';
import { AppBar, Toolbar, Typography, Button, Box } from '@mui/material';
import { Link } from 'react-router-dom';
const Header: React.FC = () => {
return (
<AppBar position="static" style={{ minWidth: '100vw', background: 'green' }}>
<Toolbar style={{ display: 'flex', justifyContent: 'space-between' }}>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Redux Toolkit
</Typography>
<Box display={{ xs: 'none', md: 'block' }}>
<Button color="inherit" component={Link} to="/">Home</Button>
<Button color="inherit" component={Link} to="/register">Register</Button>
<Button color="inherit" component={Link} to="/users">Users</Button>
</Box>
</Toolbar>
</AppBar>
);
};
export default Header;
Footer.tsx
import React from 'react';
import { Box, Typography } from '@mui/material';
const Footer: React.FC = () => {
return (
<Box component="footer" sx={{ py: 2, textAlign: 'center', mt: 'auto' }}>
<Typography variant="body2" color="text.secondary">
© {new Date().getFullYear()} My App. All rights reserved.
</Typography>
</Box>
);
};
export default Footer;
Conclusion
In this article, we created a modern React application using Vite, Redux Toolkit, Material-UI, and TypeScript. We set up a Redux store, created slices for state management, and built pages for registering users and displaying registered users. We also utilized Material-UI for styling and persisted our data using redux-persist.
#ReactJS #Vite #ReduxToolkit #MaterialUI #TypeScript #WebDevelopment---------