Building a Modern React App with Vite, Redux Toolkit, MUI, and TypeScript

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;

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.

💡
Check out the project: GitHub Repo

#ReactJS #Vite #ReduxToolkit #MaterialUI #TypeScript #WebDevelopment---------