Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

reactjs-boilerplates

A collection of React + TypeScript + MUI starter projects. The recommended flavor is with-mui/typescript-v3 — the most complete baseline with Redux Toolkit, React Query, role-based routing, Formik/Yup, theming, and ready-made window.flash / window.modal notifications.

The other versions (typescript/, typescript-v2/) are kept for history. New work should start from v3.

Quick start

cd with-mui/typescript-v3
npm install        # if peer-dep errors, use: npm install --legacy-peer-deps
npm start

Open http://localhost:3000. The CRA dev server, ESLint config, and all boilerplate wiring (providers, theme, auth bootstrap) are already in place.

Versions in this repo

Folder Status One-liner
with-mui/typescript/ v1 (legacy) Basic CRA + TypeScript + MUI. Uses CRACO for src/* path alias.
with-mui/typescript-v2/ v2 (legacy) TS + MUI + enhancements over v1.
with-mui/typescript-v3/ v3 (recommended) Full app shape: routing, auth, RBAC, theming, forms, utilities.

Use case

Reach for typescript-v3 when you want a production-shaped React 18 + TypeScript SPA in minutes, without re-deciding the boring parts:

  • UI: MUI 5 with a typed custom theme (colors, header, componentCustomStyles) and a light-theme variant ready to fork.
  • State: Redux Toolkit with typed useSelector / useDispatch plus useActions() for ergonomic action creators.
  • Server state: React Query with a useQueryState wrapper that exposes a flat foundError for cleaner conditionals.
  • Routing: react-router 6 with a split-by-role definition (auth / admin / public) and three ready-made route guards (Authenticated, Guest, Public).
  • Forms: Formik + Yup, with custom Yup methods (phone, password, confirmPassword, onlyAlphabets, alphaNumeric, length, minLength, maxLength).
  • Notifications: window.flash({ message, variant }) and window.modal({ type, ... }) — global, no prop drilling.
  • Auth: useAuth() hook (login / logout / initialize), redux-backed session, role-based access on routes.
  • Utilities: cookie, route, axios, file, date, object, array, string, number helpers — all re-exported from src/utils.

Folder structure map

reactjs-boilerplates/
├── README.md                         # this file
├── .gitignore
└── with-mui/
    └── typescript-v3/                # ← the recommended boilerplate
        ├── package.json              # scripts, deps
        ├── package-lock.json
        ├── tsconfig.json             # baseUrl + paths (src/*, @/public/*)
        ├── .eslintrc.json            # ESLint + TS + React rules
        ├── .eslintignore
        ├── .gitignore
        ├── public/                   # static assets served at /
        │   ├── index.html            # <title>Ping</title>, root div
        │   ├── manifest.json
        │   ├── robots.txt
        │   ├── favicon.ico
        │   └── logo192.png / logo512.png
        └── src/
            ├── App.tsx               # provider tree (see "Wiring" below)
            ├── index.tsx             # mounts <BrowserRouter><App /></BrowserRouter>
            ├── createEmotionCache.ts # emotion cache (key: "css")
            ├── react-app-env.d.ts
            ├── reportWebVitals.ts
            │
            ├── api/                  # API layer
            │   ├── index.ts
            │   ├── auth.ts           # class AuthApi → /auth/* on axiosInstance
            │   └── other-usables/
            │       └── auth-api-to-handle-oauth-and-credentials-auth/
            │           └── auth.ts   # reference: adds oAuth() → /auth/social
            │
            ├── assets/               # fonts, images, global SCSS
            │   ├── index.tsx         # re-exports AppLogoFullImage
            │   ├── font/Gilroy/      # OTF family (Light/Regular/Medium/...)
            │   ├── img/png/app-logo-full.png
            │   └── scss/
            │       ├── font.scss     # @font-face for Gilroy
            │       └── global.scss   # body reset, .skeleton-box, scrollbar
            │
            ├── components/           # UI building blocks
            │   ├── index.tsx
            │   ├── app-specific/     # tied to this app's pages
            │   │   ├── app-loader.tsx
            │   │   ├── card-spinner.tsx
            │   │   ├── error-boundary.tsx
            │   │   ├── error-ui.tsx
            │   │   ├── page-not-found.tsx
            │   │   └── redirect.tsx
            │   └── common/           # project-agnostic primitives
            │       ├── custom-button.tsx
            │       ├── custom-card.tsx
            │       ├── custom-link.tsx
            │       ├── custom-text.tsx
            │       ├── flash-message.tsx
            │       ├── media-query-box.tsx
            │       ├── aligned-boxes/      # FlexBox, FlexRow, GridBox,
            │       │   # XCenter, YCenter, XYCenter, JustifyBetween
            │       └── custom-modal/
            │           ├── index.tsx       # <CustomModal /> event subscriber
            │           └── components/
            │               └── confirmation-modal.tsx
            │
            ├── content/              # page-level content per route group
            │   ├── index.tsx         # HomePageContent — role-based redirect
            │   ├── admin/index.tsx   # AdminHomeContent
            │   ├── auth/
            │   │   ├── index.tsx
            │   │   ├── login.tsx     # LoginPageContent
            │   │   ├── components/
            │   │   │   └── logo.tsx
            │   │   └── other-usables/
            │   │       └── o-auth.txt        # reference: OAuthPageContent
            │   └── public/
            │       ├── index.tsx
            │       └── example.tsx
            │
            ├── data/                 # static config used app-wide
            │   ├── index.ts
            │   ├── static.ts         # authSetup, rbacSetup, projectSetup
            │   └── other-usables/
            │       ├── static.txt            # extended setup w/ MSAL
            │       └── o-auth-config.txt     # GOOGLE/MICROSOFT env config
            │
            ├── docs/                 # in-repo notes
            │   ├── setup.md
            │   ├── tips.md
            │   └── libraries/other.md
            │
            ├── guard/                # route-level authorization wrappers
            │   ├── authenticated.tsx # redirects unauthenticated → authPage
            │   ├── guest.tsx         # redirects authenticated → homePage
            │   └── public.tsx        # pass-through
            │
            ├── hooks/                # reusable React hooks
            │   ├── index.tsx
            │   ├── useAuth.ts        # wraps authApi + redux + cookies
            │   ├── useActions.ts     # bindActionCreators, typed
            │   ├── useQueryState.ts  # [data, isLoading, { foundError, ... }]
            │   ├── useUniqueKey.ts   # memoized uniqid() array for list keys
            │   └── other-usables/
            │       └── useAuth-with-oauth.txt  # reference: OAuth variant
            │
            ├── layouts/              # per-role layout shells
            │   ├── auth/             # AuthLayout, layout-settings
            │   └── admin/            # AdminLayout, layout-settings, header/
            │
            ├── model/                # shared TS types
            │   ├── index.tsx         # AUTH_DATA, AUTH_STATE, etc.
            │   └── custom-models.ts  # type Overwrite<T, U>
            │
            ├── provider/
            │   └── auth-provider.tsx # calls useAuth().initialize() once
            │
            ├── redux/                # Redux Toolkit
            │   ├── index.ts          # configureStore + typed hooks
            │   ├── actions.ts
            │   ├── reducer.ts        # combineReducers({ auth, theme })
            │   └── slices/
            │       ├── auth.ts       # initialize / login / logout
            │       └── theme.ts      # current theme name (localStorage)
            │
            ├── routes/               # routing
            │   ├── index.tsx         # top-level routes + /404 + catch-all
            │   ├── router.tsx        # <Routes> = useRoutes(routes)
            │   ├── definition/
            │   │   ├── index.tsx     # { auth, admin, public } map
            │   │   ├── auth.tsx      # /auth/login
            │   │   ├── admin.tsx     # /admin
            │   │   ├── public.tsx    # /public
            │   │   └── other-usables/auth.txt  # reference: signup + oauth
            │   └── navigation-links/
            │       ├── index.tsx
            │       └── admin.tsx
            │
            ├── schema/               # Yup schemas (one per feature)
            │   └── index.ts          # empty placeholder — add yours here
            │
            ├── theme/                # MUI theming
            │   ├── index.tsx
            │   ├── ThemeProvider.tsx
            │   ├── utils.ts          # THEME_NAMES, themeCreator, types
            │   ├── viewport.tsx      # BREAKPOINTS, mediaQuery.up/down
            │   ├── variants/
            │   │   └── pure-light-theme.ts
            │   └── icons/
            │       └── google.tsx    # <GoogleIcon />
            │
            └── utils/                # pure helpers (all re-exported)
                ├── index.ts          # barrel
                ├── api-utils.ts      # axiosInstance, getError, etc.
                ├── cookie-utils.ts   # getCookie/setCookie/deleteCookie
                ├── route-utils.ts    # isActiveRoute
                ├── window-utils.ts   # EventEmitter + window.flash / .modal
                ├── library-utils.ts  # yup custom methods + { yup, uniqId }
                ├── validation-utils.js  # isRequiredField (Formik-aware)
                ├── object-utils.js   # convertDropDownObject, dot-notation
                ├── array-utils.js    # globalSearch, removeDuplicates, ...
                ├── string-utils.js   # millify, plurify, isValidValue
                ├── number-utils.js   # filterNumbers, numberWithCommas
                ├── date-utils.js     # getDateCollapsed, isDateBefore, ...
                └── file-utils.js     # compressFile, getBase64, downloadLink

Tech stack

  • React 18.2 + TypeScript 4.7
  • MUI 5 (@mui/material, @mui/lab, @mui/icons-material) + Emotion for styling
  • Redux Toolkit 1.8 + react-redux 8
  • React Query 3.39
  • react-router-dom 6.3
  • Formik 2.2 + Yup 0.32 + yup-phone
  • notistack 2 (snackbars), react-helmet 6 (document titles)
  • axios 0.27 (with a configured instance and auth-header interceptor)
  • react-phone-number-input, date-fns, compress.js, pluralize, uniqid, validator, csstype, web-vitals
  • ESLint 8 with @typescript-eslint and eslint-plugin-react
  • react-scripts 5 (CRA 5)

See with-mui/typescript-v3/package.json for exact versions and the full dev list.

Features

1. MUI 5 + custom theme system (src/theme/)

A typed theme object with colors, general, header, and a componentCustomStyles map. Module-augmented into MUI's Theme and ThemeOptions interfaces in src/theme/utils.ts. The active variant is held in Redux and persisted to localStorage.theme; <ThemeProvider> in src/theme/ThemeProvider.tsx reads it and passes the resolved createTheme(...) to MUI.

2. Themed typography — CustomText (src/components/common/custom-text.tsx)

A drop-in replacement for MUI's Typography that pulls its style from theme.componentCustomStyles[variant]. Supports h1h6, subtitle1/2, body1/2, caption, button, overline, default p, and label (renders as FormLabel). Colors are looked up by name from theme.colors.

3. CustomButton (src/components/common/custom-button.tsx)

A Button with built-in loading state (CircularProgress start icon), optional href (string for a plain link, or { to, options } for a react-router navigate), and a linkStyle mode that turns it into a text-only link.

4. CustomCard (src/components/common/custom-card.tsx)

Card with optional customHeader, cardActions, headerProps, and loading (renders CardSpinner instead of children).

5. Layout helpers — src/components/common/aligned-boxes/

Zero-config flex/grid wrappers: FlexBox, FlexRow, GridBox, XCenter, YCenter, XYCenter, JustifyBetween. Each accepts a style override.

6. MediaQueryBox (src/components/common/media-query-box.tsx)

Wraps a div and injects a media-query rule via the mediaQuery.up() or mediaQuery.down() helper from src/theme/viewport.tsx. Useful when you need a one-off responsive tweak without leaving JSX.

7. Global flash messages — window.flash

A single <FlashMessage /> is mounted in App.tsx and listens to an EventEmitter event. Anywhere in the app you can call:

window.flash({ message: "Saved", variant: "success" });
// variants: default | success | error | warning | info

Wired in src/utils/window-utils.ts (createEventEmitters), consumed in src/components/common/flash-message.tsx (notistack).

8. Global modal — window.modal

A single <CustomModal /> mounted in App.tsx listens to a second EventEmitter event. Open any component as a modal, or use the built-in confirmation dialog:

// custom component
window.modal({
  type: "custom",
  component: MyComponent,
  containerProps: { closeOnClick: true },
  contentContainerProps: { sx: { width: 480 } },
});

// confirmation
window.modal({
  type: "confirmation",
  title: "Delete this item?",
  description: "This cannot be undone.",
  onConfirm: async () => { await api.delete(id); },
  onCancel: () => {},
});

Wired in src/utils/window-utils.ts, consumed in src/components/common/custom-modal/.

9. React Query with useQueryState (src/hooks/useQueryState.ts)

A single QueryClient is created in App.tsx. Prefer the local wrapper for ergonomic destructuring:

const [data, isLoading, { foundError, refetch }] = useQueryState({
  queryKey: ["users", page],
  queryFn: () => api.getUsers(page),
});
// `foundError` is non-null only when the query is settled AND errored
// — no need to guard against transient errors during refetch.

10. Redux Toolkit + typed hooks (src/redux/, src/hooks/)

useSelector and useDispatch are re-typed in src/redux/index.ts and re-exported from src/hooks/index.tsx. useActions() returns a flat object of bound action creators:

const { authActions, theme } = useActions();
authActions.login(userData);
theme.changeTheme("pure-light-theme");

serializableCheck is disabled in the store config to allow non-serializable values in actions.

11. Auth + RBAC + route guards (src/hooks/useAuth.ts, src/guard/)

useAuth() exposes login, logout, initialize, plus the redux auth slice (isAuthenticated, isInitialized, data).

const { login, logout, data, isAuthenticated } = useAuth();

await login({ email, password });        // sets cookie + redux
await logout();                          // clears cookie + redux + reload
await initialize();                      // call once on app boot

Three route-level guards:

  • <Authenticated roles={["admin"]}> — redirects unauthenticated users to authSetup.authPage?backToURL=<path>; redirects role mismatches to /404.
  • <Guest> — redirects authenticated users to authSetup.homePage (used for login/signup pages).
  • <Public> — pass-through.

Static config (authSetup, rbacSetup, projectSetup) lives in src/data/static.ts.

12. Form helpers (Formik + Yup) — src/utils/library-utils.ts

Yup is augmented with custom methods. Import { yup, uniqId } from src/utils:

import { yup } from "src/utils";

const schema = yup.object({
  email: yup.string().email().required(),
  phone: yup.string().phone().required(),            // react-phone-number-input
  password: yup.string().password().required(),      // 8+, upper/lower/digit/special
  confirm: yup.string().confirmPassword("password").required(),
  name: yup.string().onlyAlphabets().required(),
  code: yup.string().alphaNumeric().required(),
  pin: yup.number().length(6).required(),
});

isRequiredField(yupSchema, "address.city") from src/utils/validation-utils.js returns whether a (possibly nested) field is .required() — useful for rendering a * next to a label.

Yup schemas go in src/schema/ (one file per feature; the directory is intentionally empty so you can grow it as you add forms).

13. axios instance + helpers (src/utils/api-utils.ts)

axiosInstance is created with a baseURL and a request interceptor that attaches Authorization: Bearer <token> from the cookie named by authSetup.tokenAccessor. A 401 response interceptor is left commented out as a drop-in.

import { axiosInstance, createApiFunction, handleError } from "src/utils";

const data = await createApiFunction(() => axiosInstance.get("/users"));
//   ↳ resolves with response.data; passes a thrown error through.

try { await api.something(); }
catch (err) { handleError(err); } // → window.flash({ variant: "error", ... })

14. Cookie helpers (src/utils/cookie-utils.ts)

setCookie("token", value, { "max-age": 3600, secure: true });
const token = getCookie("token");
deleteCookie("token");

15. Utility modules (barrel re-exported from src/utils/index.ts)

A few highlights:

// dropdown <-> backend shape
import { convertDropDownObject } from "src/utils";
const option = convertDropDownObject({
  value: user,
  valueAccessor: "_id",
  labelAccessor: "name",
});
// → { value: "...", label: "..." }

// dot-notation read/write
import { accessValueByDotNotation, modifyObjectByDotNotation } from "src/utils";
const city = accessValueByDotNotation(user, "address.city");
modifyObjectByDotNotation(user, "address.city", "Chennai");

// dedupe + validate arrays of objects
import { removeDuplicates, validateArrayOfObjects } from "src/utils";
const unique = removeDuplicates(rows, { keys: ["email"] });
await validateArrayOfObjects(rows, {
  requiredFieldKeys: ["email"],
  removeDuplicates: true,
  duplicationOptions: { keys: ["email"], detectExactDuplicate: false },
  validationTypes: { email: "email", mobile: "number" },
});

// number / string / date helpers
import {
  numberWithCommas, filterNumbers, millify, plurify, isValidValue,
  getDateCollapsed, isDateBefore, isDateAfter, isBetweenDate,
} from "src/utils";

16. File helpers (src/utils/file-utils.js)

import { compressFile, getBase64, downloadLink } from "src/utils";

const out = await compressFile([file], { size: 2, quality: 0.75 }); // MB
const dataUrl = await getBase64(file);
downloadLink({ link: fileUrl, name: "report.pdf" });

compressFile is browser-only (lazy require("compress.js")). It also returns each entry with a dataWithPrefix (e.g. data:image/jpeg;base64,...) for direct <img src> use.

17. Routing — split by audience (src/routes/)

Routes are defined per role in src/routes/definition/<role>.tsx and combined in src/routes/definition/index.tsx. The top-level src/routes/index.tsx assembles them with <Helmet> titles, guards, layouts, and a /404 + catch-all * route.

To add a new admin page:

  1. Add the component in src/content/admin/.
  2. Append a ROUTE_DEFINITION to src/routes/definition/admin.tsx.
  3. If it needs nav, add it to src/routes/navigation-links/admin.tsx.

18. Layouts per role (src/layouts/)

AuthLayout and AdminLayout are the two shells. AdminLayout already embeds a CustomButton that calls useAuth().logout, hidden on the signup route via isActiveRoute. Each layout has a sibling layout-settings.tsx (e.g. header height) used by both the layout itself and any inner content that needs to compensate for it.

19. Redirect helper (src/components/app-specific/redirect.tsx)

<Redirect to="/login" />

Renders null and calls useNavigate(to, { replace: true }) once on mount. Useful for declarative redirects inside other components.

20. Linting (with-mui/typescript-v3/.eslintrc.json)

npm run lint        # eslint src/**/*.tsx
npm run lint:fix    # same, with --fix

Config extends eslint:recommended, plugin:react/recommended, plugin:@typescript-eslint/recommended, and plugin:react/jsx-runtime. Enforces double quotes and semicolons. .eslintignore covers node_modules, dist, build.

Customization

What you want to change → where to change it.

Want to change Edit
App name / brand projectSetup.title in src/data/static.ts, <title> in public/index.html, and the <Helmet> titles in src/routes/definition/*.tsx
App logo Replace src/assets/img/png/app-logo-full.png (re-exported as AppLogoFullImage from src/assets/index.tsx)
API base URL baseURL in src/utils/api-utils.ts — currently hard-coded. Recommended: move to process.env.REACT_APP_API_URL (create a .env file) or to projectSetup.baseURL
Default theme projectSetup.defaultTheme in src/data/static.ts; the literal in src/redux/slices/theme.ts (TODO) should also be updated
Theme tokens (colors, header, typography) src/theme/variants/pure-light-theme.ts
Add a new theme variant Create a file in src/theme/variants/, re-export from src/theme/variants/index.tsx, register it in the customThemes map and the THEME_NAMES enum in src/theme/utils.ts
Default phone country projectSetup.defaultPhonenumberCountry in src/data/static.ts
Auth/login/signup pages, token cookie name authSetup in src/data/static.ts
RBAC roles and per-role home page rbacSetup in src/data/static.ts
Fonts Drop new files into src/assets/font/<family>/, declare @font-face in src/assets/scss/font.scss, update body { font-family } in src/assets/scss/global.scss
Global SCSS / scrollbar / skeleton src/assets/scss/global.scss
Add a new role New src/routes/definition/<role>.tsx + entry in src/routes/definition/index.tsx + src/routes/navigation-links/<role>.tsx + src/layouts/<role>/ + a <Authenticated roles={...}> guard usage
Public favicon / PWA assets public/favicon.ico, public/logo192.png, public/logo512.png, public/manifest.json
HTTP client error handling src/utils/api-utils.ts (the 401 response interceptor is commented out)

The other-usables/ convention

Several folders ship a sibling other-usables/ directory whose files are *.txt (not .ts / .tsx) so TypeScript excludes them from the build:

  • src/api/other-usables/auth-api-to-handle-oauth-and-credentials-auth/auth.ts
  • src/content/auth/other-usables/o-auth.txt
  • src/data/other-usables/o-auth-config.txt
  • src/data/other-usables/static.txt
  • src/hooks/other-usables/useAuth-with-oauth.txt
  • src/routes/definition/other-usables/auth.txt

These are reference implementations for features that are intentionally opt-in — most notably OAuth (Google / Microsoft) and sign-up. They are not imported anywhere.

To enable one of them:

  1. Open the .txt file.
  2. Copy its contents into the matching live file (e.g. overwrite src/api/auth.ts, or extend src/data/static.ts with the values from static.txt).
  3. Adjust as needed for your project.

Scripts

Run from with-mui/typescript-v3/:

Script What it does
npm start CRA dev server on http://localhost:3000
npm run build Production build to build/
npm test Jest via CRA (no extra tests are committed)
npm run eject CRA eject (one-way) — you almost certainly do not want this
npm run lint eslint src/**/*.tsx
npm run lint:fix same, with --fix

Path aliases

tsconfig.json declares:

{
  "baseUrl": ".",
  "paths": {
    "src/*": ["./src/*"],
    "@/public/*": ["./public/*"]
  }
}

so every import in the boilerplate is written as import { foo } from "src/...". TypeScript and your editor resolve these aliases natively.

Runtime caveat: react-scripts 5 does not honor tsconfig.paths at build time without a custom config (e.g. CRACO). The v1 boilerplate (with-mui/typescript/) ships a craco.config.ts for this. v3 does not include one yet — the existing tsconfig.json covers editor / type-check time only. If you need runtime aliasing, add CRACO or an equivalent.

Known gaps / TODOs

These are flagged in the code and called out so you can decide how to handle them:

  • axiosInstance.baseURL is hard-coded in src/utils/api-utils.ts to a dev URL. Move it to an env var or projectSetup.baseURL before shipping.
  • 401 response interceptor is commented out at the bottom of src/utils/api-utils.ts — uncomment and adjust for your auth flow.
  • theme.ts slice has a TODO about a circular dependency with src/theme (it can't import THEME_NAMES cleanly).
  • tsconfig is loose: strict: false and strictNullChecks: false. Some legacy .js files in src/utils/ rely on this (allowJs: true).
  • src/index.tsx uses React 17 ReactDOM.render. A commented React 18 createRoot block sits above it with a note about a re-render quirk. Swap them when you're ready.
  • src/data/static.ts defaults still reference the dev URL (http://localhost:5000) and "IN" as the default phone country.

References

About

A collection of React + TypeScript + MUI starter projects. The recommended flavor is with-mui/typescript-v3 — the most complete baseline with Redux Toolkit, React Query, role-based routing, Formik/Yup, theming, and ready-made window.flash / window.modal notifications.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages