@dauphaihau/react-template
v1.3.5
Published
A modern Vite React starter template with TanStack Router, TanStack Query, and Tailwind CSS. Built with TypeScript, Feature-Sliced Design, and best practices.
Maintainers
Readme
@dauphaihau/react-template
A Vite + React 19 starter template with TypeScript, TanStack Router, TanStack Query, Tailwind CSS, and a module-based folder structure.
This package is the template source used by the create-react-template CLI.

Architecture Influences
This template is not a strict implementation of a single architecture. It combines a few practical ideas:
- Feature-Sliced Design: inspired by one-way dependency flow and separation between app, module, and shared code.
- Clean Architecture: inspired by separation of concerns and keeping bootstrap code away from reusable logic.
- Colocation: related files stay close to the module or component that owns them.
- Modular architecture: business areas live in
modules/, while reusable infrastructure lives inshared/. - File-based routing: TanStack Router owns route structure under
app/router/routes/.
What's Included
- React 19
- TanStack Router file-based routing
- TanStack Query for server state
- Tailwind CSS v4
- TypeScript
- Vitest
- ESLint
- Husky and lint-staged
- Error boundary with fallback UI
- Zod-validated environment variables
- Theme provider and toggle with light and dark modes
- Module-based folder structure
- Shared client-state folder for app-level client state
Using This Template
This package is consumed by the create-react-template CLI.
To scaffold a new project, use the published CLI package:
bunx @dauphaihau/create-react-template my-appOr with npm:
npx @dauphaihau/create-react-template my-appFor full CLI usage and local development commands, see the CLI README.
Optional Features
The create-react-template CLI can scaffold additional features on top of the base template.
Current optional features include:
- Zustand: adds a persisted client-state store example for the lab app shell sidebar
When selected, the Zustand feature adds:
src/shared/client-state/app-shell/
├── app-shell.store.ts
└── index.tsIt also wires the lab route layout to use the store for sidebar open and close state.
Template Structure
src/
├── app/
│ ├── index.tsx # Entry point, mounts the React app
│ ├── providers/ # App-level providers, such as ThemeProvider
│ ├── router/ # Router factory, generated route tree, and route files
│ │ ├── index.ts # Router factory
│ │ ├── routeTree.gen.ts # Auto-generated by TanStack Router
│ │ └── routes/ # File-based route components
│ └── styles/
│ └── styles.css # Global Tailwind styles
├── modules/ # Self-contained feature or business-area modules
│ └── blog/
│ ├── api/ # Module-local API clients and DTOs
│ ├── server-state/ # Module-local TanStack Query operations
│ └── index.ts # Public module exports
└── shared/ # Cross-cutting utilities and infrastructure
├── client-state/ # Shared client state and optional client-state feature scaffolds
├── config/ # Shared app configuration
├── error/ # Shared error types and error logging helpers
├── hooks/ # Generic shared React hooks
├── lib/ # App config, third-party setup, and generic utilities
└── ui/ # Shared UI components
├── app/ # App-aware shared UI, such as ErrorBoundary and ThemeToggle
└── primitives/ # Generic primitive UI componentsRouting
Routes live in src/app/router/routes/ and are handled by TanStack Router file-based routing.
routes/
├── __root.tsx
├── index.tsx
├── getting-started/
│ ├── route.tsx
│ ├── index.tsx
│ └── _components/
└── lab/
├── route.tsx
├── index.tsx
├── test-error.tsx
└── blog/
├── index.tsx
└── $slug.tsxKeep route files focused on routing concerns, data loading, metadata, and page composition. Move reusable logic into modules/ or shared/.
Module Boundaries
Imports flow downward through the app:
app -> modules -> sharedapp/contains application bootstrap, routing, providers, and global styles.modules/contains feature or business-area code owned by a specific module.shared/contains code reused across routes or modules.
Modules should not depend on each other directly. If code is needed by multiple modules, move it to shared/.
App-level providers live in app/providers/ and are wired from app/index.tsx. Shared provider contracts, such as theme context, live in shared/client-state/ so both app bootstrap and modules can depend on them without creating module-to-module imports.
For example, if two modules need the same app-aware component, place it under shared/ui/app/ instead of copying it into both modules:
shared/ui/app/
└── account-status-badge/
├── account-status-badge.tsx
└── index.tsIf the component is a generic UI primitive with no app knowledge, place it under shared/ui/primitives/ instead.
Naming Conventions
- File and directory names use lowercase kebab-case:
theme-toggle.tsx,error-display/. - React component symbols use PascalCase:
ThemeToggle,ErrorBoundary. - Test files live next to the code they cover:
button.test.tsx,utils.test.ts.
Component Structure
Use the smallest structure that keeps the component readable.
Self-contained components stay as a single file:
modules/account/
└── account-status.tsxUse a folder when the component has supporting UI pieces or local support files:
modules/account/components/account-menu/
├── index.ts # Re-export only
├── account-menu.tsx # Root component logic
├── account-menu-item.tsx
├── account-menu-footer.tsx
├── constants.ts # Local labels, config, limits, or option lists
├── types.ts # Local types only
└── utils.ts # Pure local helpersUse a nested folder when a child component has its own subcomponents or support files:
modules/account/components/account-menu/
├── index.ts
├── account-menu.tsx
└── account-menu-section/
├── index.ts
├── account-menu-section.tsx
├── account-menu-section-item.tsx
└── types.tsDo not create extra files for trivial content. index.ts is re-export only; do not put JSX or component logic there.
State and Data
- Server state query options, hooks, and mutations belong in the owning module, for example
modules/blog/server-state/. - Shared client-only state belongs in
shared/client-state/. - The base template keeps this area minimal; optional CLI features such as Zustand can scaffold concrete stores here.
- Generic reusable React hooks belong in
shared/hooks/; server-state hooks stay with their module query or mutation operation. - Domain API access and DTOs belong in the owning module, for example
modules/blog/api/. - Shared API client setup belongs in
shared/lib/. - Shared error types and error logging helpers belong in
shared/error/. - Module-local UI behavior belongs inside the owning module or route-local
_components/folder.
Group server-state files by module and operation:
modules/blog/server-state/
├── index.ts
├── query-keys.ts
├── mutations/
│ └── create-blog.mutation.ts
└── queries/
├── get-blog.query.ts
└── get-blogs.query.tsEach module should keep query keys local to that module. Use query option helpers for exact query reuse, and the module key factory for partial invalidation groups.
queryClient.invalidateQueries({ queryKey: blogQueryKeys.all });
queryClient.invalidateQueries({ queryKey: blogQueryKeys.lists() });Each query operation can export both query options and a React hook. Keep query options separate when the same operation may be reused by non-component code such as router loaders, prefetching, queryClient.ensureQueryData, or exact cache invalidation:
queryClient.prefetchQuery(getBlogQueryOptions(id));
queryClient.ensureQueryData(getBlogQueryOptions(id));
queryClient.invalidateQueries({ queryKey: getBlogQueryOptions(id).queryKey });Mutation operations live beside query operations and should invalidate the smallest useful domain key:
export function useCreateBlogMutation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (payload: CreateBlogPostDto) => blogApi.create(payload),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: blogQueryKeys.lists() });
},
});
}Group API files by module. Keep DTOs separate from endpoint functions so large response contracts do not bloat transport code:
modules/blog/api/
├── index.ts
├── dto.ts
├── blog.api.ts
└── blog.api.test.tsSmall endpoint groups can be exposed as one object:
blogApi.list();
blogApi.detail(id);
blogApi.create(payload);Path Alias
The #/ alias maps to src/:
import { Button } from '#/shared/ui';
import { useErrorHandler } from '#/shared/hooks/use-error-handler';
import { ThemeToggle } from '#/shared/ui/app';
import { queryClient } from '#/shared/lib/query-client';Scripts
Inside a generated project:
bun run dev
bun run build
bun run test
bun run lintRelated Package
The CLI is published as @dauphaihau/create-react-template.
License
MIT
