A small React + TypeScript + Tailwind + shadcn template focused on a clear routing and layout pattern.
This template separates route definitions from route-to-UI wiring and groups routes by layout. Routes are lazy-loaded and rendered via React Router using a small helper that maps logical route config into route objects.
src/config/routes.tsx— central route definitions (path, lazy component import, optionallayout,children).src/helpers/route.helper.tsx— converts route config into React Router route objects and wires layouts.src/config/layout.config.ts— maps layout names to layout components (lazy-loaded).src/config/app.config.ts— application-level defaults (includingdefaultLayout).src/layouts/*— layout components (each exposes anOutletfor nested routes).src/App.tsx— mounts React Router and uses the generated routes.
-
Define routes in
src/config/routes.tsxas an array of objects with these fields:path: the route path (e.g./,/about).component: a function returning a lazy import:() => import('@/views/Home').layout(optional): name of the layout to render this route with. If omitted,APP_CONFIG.defaultLayoutis used.children(optional): nested route configs.
-
createRoutesinsrc/helpers/route.helper.tsxgroups routes bylayoutand returns React RouterRouteObjects. Each layout becomes a parent route whoseelementis the layout component and whosechildrenare the grouped routes. -
Routes'
elementvalues are lazy-wrapped withReact.lazy+Suspense(there's aLoadingFallback) so each view is code-split and loaded on demand. -
src/App.tsxcallscreateRoutes(routeConfig)and renders them viauseRoutesinside aBrowserRouter.
{
path: '/properties',
component: () => import('@/views/Properties'),
layout: 'Default', // optional — if omitted, default from app.config is used
}
- Layouts live under
src/layouts/and should export a default React component that renders anOutletfromreact-router-dom. src/layouts/default/index.tsx(Default layout) composesHeader,Footer, and anOutletfor page content.src/layouts/plain/index.tsx(Plain layout) is a minimal container that just renders anOutlet.- The layout mapping lives in
src/config/layout.config.tswhere each layout is associated with a lazy import.
- Create
src/layouts/YourLayout/index.tsxand make sure it renders<Outlet />where page content should appear. - Add an entry to
src/config/layout.config.tsmapping your layout name (e.g.MyLayout) to a lazy import:MyLayout: lazy(() => import('@/layouts/mylayout')). - Use
layout: 'MyLayout'on route entries that should use it.
Nested children in a route config will be converted recursively by createRoutes so you can define nested URL structures and nested layouts as needed. Example:
{
path: '/account',
component: () => import('@/views/Account'),
children: [
{ path: 'settings', component: () => import('@/views/AccountSettings') },
],
}
- Add a view file under
src/views/YourPage.tsx. - Add a route to
src/config/routes.tsxwith a lazycomponentimport and optionallayout. - If using a new layout, register it in
src/config/layout.config.ts.
- Routes:
src/config/routes.tsx - Route helper:
src/helpers/route.helper.tsx - Layout mapping:
src/config/layout.config.ts - Default config:
src/config/app.config.ts - Layouts:
src/layouts/
If you'd like, I can also add a short example page and a sample custom layout to the template — want me to add those now?
The main application configuration lives in src/config/app.config.ts as the APP_CONFIG constant. This file centralizes defaults and environment-driven values used across the app.
Key uses and fields:
defaultLayout: the layout name used when a route doesn't explicitly setlayout. The routing helper uses this to group routes.landingPage: the default landing route (e.g./).defaultAuthenticatedRoute: where authenticated users are redirected by default (e.g./properties).baseUrl: API base URL; often sourced fromimport.meta.env.VITE_API_BASE_URL.timeout,apiKey, and analytics fields: useful for centralizing API/timeouts and third-party integrations.
Example: import and use APP_CONFIG in a component or util
import { APP_CONFIG } from '@/config/app.config'
console.log('API base:', APP_CONFIG.baseUrl)
// use default layout name
const defaultLayout = APP_CONFIG.defaultLayout
Environment variables
- Prefix environment variables with
VITE_in Vite (e.g.VITE_API_BASE_URL) and reference them inapp.config.tsas shown in the template.
Customizing defaults
- To change the default layout for the app, set
defaultLayoutto the layout key you added tosrc/config/layout.config.ts. - To change API endpoints or keys per-environment, define
VITE_API_BASE_URLand otherVITE_*vars in your.envfiles.
Where it matters
- Routing falls back to
APP_CONFIG.defaultLayoutwhen a route has no explicitlayout. - Any global helpers, services, or components can import
APP_CONFIGto read app-level settings.