Welcome to the Gaia React TypeScript Template! This boilerplate is designed to help you quickly set up a modern, scalable React application with TypeScript. It includes essential tools, best practices, and configurations to streamline your development process.
- Features
- Getting Started
- Project Structure
- API Structure
- Components Structure
- Hooks Structure
- Libs Structure
- Pages Structure
- Providers Structure
- Translation Structure
- Utils Structure
- Prettier Configuration in VSCode
- General Information
- React 19 – Build interactive and efficient UIs with the latest version of React.
- TypeScript – Enhance code quality and maintainability with static typing.
- React Router – Manage navigation seamlessly using
react-router-dom. - React Query – Handle data fetching and caching efficiently with
@tanstack/react-query. - React Hook Form – Simplify form handling and validation.
- Zod – Schema validation for robust TypeScript applications.
- i18next – Easily add internationalization support.
- ESLint & Prettier – Maintain clean and consistent code formatting.
- Storybook – Develop UI components in isolation.
- Jest & React Testing Library – Set up unit and integration testing.
- Axios – Make API requests seamlessly.
This is a private template repository, so you can create a new repository from it using GitHub:
- Go to the repository on GitHub.
- Click "Use this template" in the top-right corner.
- Select "Create a new repository".
- Enter your repository details (name, description, visibility, etc.).
- Click "Create repository".
Once your repository is created, you can clone it and start working.
Alternatively, you can manually clone and set up a new repository:
# Clone the repository
git clone https://github.com/gaia-ia/gaia-react-ts-template.git my-new-project
# Navigate into the project folder
cd my-new-project
# Remove the existing Git history
rm -rf .git
# Initialize a new Git repository
git init
# Add all files to the new repository
git add .
# Commit the initial files
git commit -m "Initial commit from Gaia React TS Template"
# Set up a new remote repository (replace with your repo URL)
git remote add origin <your-repository-url>
# Push to your repository
git push -u origin mainOnce your repository is set up, install the required dependencies:
pnpm installRun the following command to launch the development server:
pnpm startThis starts a local server and opens your application in the browser.
To generate an optimized production build, run:
pnpm buildThis creates a build/ folder with compiled files ready for deployment.
You're now ready to start developing with the Gaia React TypeScript Template! 🚀
This template is organized following a modular and scalable architecture to enhance maintainability and reusability:
src/
├── App.tsx
├── Routes.tsx
├── api/ # API request handlers
│ └── advice/
│ └── index.tsx
├── assets/ # Static assets (images, icons, etc.)
│ ├── icons/
│ │ ├── index.ts
│ │ └── logos/
│ │ └── SquareLogo.tsx
│ └── images/
│ └── labyrinth.jpg
├── components/ # Reusable UI components
│ ├── buttons/
│ ├── general/
│ ├── inputs/
│ ├── typography/
│ ├── index.ts
├── hooks/ # Custom React hooks
│ ├── index.tsx
│ └── useFetch/
├── i18n.tsx # Internationalization setup
├── index.css # Global styles
├── index.tsx # Application entry point
├── libs/ # Third-party library configurations
│ ├── axios/
│ └── react-query/
├── pages/ # Page components mapped to routes
│ ├── Homepage/
│ ├── Login/
│ │ ├── components/
│ │ │ ├── LoginForm/
│ │ ├── pages/
│ │ │ ├── SignUp/
│ │ │ ├── PasswordReset/
│ │ ├── index.tsx
│ │ ├── styles.module.css
├── providers/ # Context providers for state management
│ ├── ReactQueryProvider/
│ ├── ThemeProvider/
│ └── index.tsx
├── translation/ # Localization files
│ ├── en/
│ ├── es/
│ └── pt/
└── utils/ # General utility functions
├── ProtectedRoute/
└── env/
Each folder inside api/ represents a group of API requests. These requests are handled using react-query and axios to simplify data fetching and caching.
import { useFetch } from '@hooks';
type TAdvice = {
slip: {
id: number;
advice: string;
};
};
export const useGetAdvice = () => useFetch<TAdvice>(['advice'], '/advice');The components/ folder organizes reusable UI components into logical groups. Each group is a folder that contains specific components.
components/
├── buttons/
│ ├── Button/
│ │ ├── index.tsx
│ │ ├── styles.module.css
│ │ ├── types.ts
│ │ └── index.stories.tsx
│ └── ButtonLink/
│ ├── index.tsx
│ ├── styles.module.css
│ ├── types.ts
│ └── index.stories.tsx
├── general/
├── inputs/
├── typography/
├── index.ts
Each component inside a group has its own folder containing:
index.tsx: The component implementation.styles.module.css: The component-specific styles.types.ts: Type definitions for the component props.index.stories.tsx: Storybook configuration for UI testing and previewing.
This file exports all components for better organization and ease of import across the project. Instead of importing components individually, you can use:
import { Button, ButtonLink } from '@components';The hooks/ folder contains reusable custom React hooks. Each hook is stored in its own folder, named in camelCase and always prefixed with use to follow React conventions.
hooks/
├── index.tsx
├── useFetch/
│ ├── index.tsx
import { api } from '@libs/axios';
import {
UseQueryOptions,
UseQueryResult,
useQuery,
} from '@tanstack/react-query';
export function useFetch<TData>(
queryKey: string[],
endpoint: string,
options?: Omit<UseQueryOptions<TData, Error>, 'queryKey' | 'queryFn'>
): UseQueryResult<TData> {
return useQuery<TData>({
queryKey,
queryFn: async () => {
const res = await api.get<TData>(endpoint);
return res.data;
},
...options,
});
}
export default useFetch;The index.tsx file inside the hooks/ folder acts as a central export file to keep imports clean and organized.
export { default as useFetch } from './useFetch';
export { default as useAuth } from './useAuth';Instead of importing hooks individually, you can import them more efficiently:
import { useAuth, useFetch } from '@hooks';The libs/ folder contains configurations for third-party libraries used within the project. Each library has its own folder, named in kebab-case, containing an index.tsx file that manages its configuration.
libs/
├── axios/
│ ├── index.tsx
├── react-query/
│ ├── index.tsx
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient();- Each library is encapsulated within its own folder, making configurations modular and easy to manage.
- Consistent kebab-case naming keeps folder names uniform.
- Encapsulated configurations ensure that global setups, such as query clients or axios instances, are centralized.
The pages/ directory contains the application's main views. Each page follows the PascalCase convention and resides in its dedicated folder, typically containing:
index.tsx– The main page component.styles.module.css– Page-specific styles.
pages/
├── Login/
│ ├── components/
│ │ ├── LoginForm/
│ ├── pages/
│ │ ├── SignUp/
│ │ ├── PasswordReset/
│ ├── index.tsx
│ ├── styles.module.css
Application routes are managed in Routes.tsx. When a page has child pages, its routing logic is handled within the page's index.tsx.
import Homepage from '@pages/Homepage';
import Login from '@pages/Login';
import ProtectedRoute from '@utils/ProtectedRoute';
import { BrowserRouter, Route, Routes } from 'react-router';
const AppRoutes = () => {
const protectedRoutes = [
{
path: '/',
element: <Homepage />,
},
];
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
{protectedRoutes.map(route => (
<Route
key={route.path}
path={route.path}
element={<ProtectedRoute>{route.element}</ProtectedRoute>}
/>
))}
</Routes>
</BrowserRouter>
);
};
export default AppRoutes;<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="signup" element={<SignUp />} />
<Route path="forgot" element={<PasswordReset />} />
</Routes>The providers/ folder is used to manage application-wide state and context providers. Each provider is stored in its own folder, named in PascalCase, containing:
index.tsx: The provider component configuration.
Additionally, the providers/index.tsx file acts as a Core Provider, wrapping all necessary providers together to keep App.tsx clean and organized.
providers/
├── ReactQueryProvider/
│ ├── index.tsx
├── ThemeProvider/
│ ├── index.tsx
├── index.tsx
import { queryClient } from '@libs/react-query';
import { QueryClientProvider } from '@tanstack/react-query';
type Props = {
children: React.ReactNode;
};
const ReactQueryProvider: React.FC<Props> = ({ children }) => {
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
};
export default ReactQueryProvider;import ReactQueryProvider from './ReactQueryProvider';
import ThemeProvider from './ThemeProvider';
type Props = {
children: React.ReactNode;
};
const CoreProvider: React.FC<Props> = ({ children }) => {
return (
<ThemeProvider>
<ReactQueryProvider>{children}</ReactQueryProvider>
</ThemeProvider>
);
};
export default CoreProvider;import { Header } from '@components';
import CoreProvider from '@providers';
import Routes from './Routes';
import './i18n';
const App = () => (
<CoreProvider>
<Header />
<Routes />
</CoreProvider>
);
export default App;The translation/ folder handles the application's internationalization. Each language has its own folder, named using its respective language code (en/, es/, pt/), and contains a translation.json file with the translated strings.
translation/
├── en/
│ ├── translation.json
├── es/
│ ├── translation.json
├── pt/
│ ├── translation.json
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import translationEN from './translation/en/translation.json';
import translationES from './translation/es/translation.json';
import translationPT from './translation/pt/translation.json';
i18n.use(initReactI18next).init({
resources: {
en: {
translation: translationEN,
},
es: {
translation: translationES,
},
pt: {
translation: translationPT,
},
},
lng: localStorage.getItem('i18nextLng') || 'en',
fallbackLng: localStorage.getItem('i18nextLng') || 'en',
interpolation: {
escapeValue: false,
},
});
export default i18n;To add a new language:
- Create a new folder inside
translation/with the appropriate language code (e.g.,fr/for French). - Add a
translation.jsonfile inside the new folder. - Update the
resourceskey ini18n.tsxto include the new language.
The utils/ folder contains helper functions and utility configurations that are used across the application. Each utility has its own folder, containing an index.tsx file where its logic is implemented.
utils/
├── ProtectedRoute/
│ ├── index.tsx
├── env/
│ ├── index.tsx
import { z } from 'zod';
export const envSchema = z.object({
REACT_APP_API_URL: z.string(),
});
export const env = envSchema.parse(process.env);- Each utility is encapsulated in its own folder, making it modular and reusable.
- New utilities can be easily added by creating a new folder in
utils/and defining their logic inside anindex.tsxfile.
To ensure consistent code formatting, configure Prettier in VSCode by following these steps:
Install Prettier - Code formatter from the VSCode Marketplace: Prettier - Code formatter
- Open the Command Palette (
Cmd + Shift + Pon macOS,Ctrl + Shift + Pon Windows/Linux). - Search for
Preferences: Open User Settings (JSON)and select it. - Add or update the following configuration:
{
// Other existing settings
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnSave": true,
"editor.tabSize": 2,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true
}Restart VSCode to apply the changes. Prettier will now automatically format your code on save and paste, ensuring consistent styling throughout the project.
- Folders: Generally use kebab-case (e.g.,
api/,libs/,utils/). - Hooks: Use camelCase, always prefixed with
use(e.g.,useFetch/). - Components & Pages: Use PascalCase (e.g.,
LoginForm/,Homepage/). - CSS Files: Named as
styles.module.cssoranything.module.css. - Stories: Located inside each component folder, named
index.stories.tsx.
- CSS Classes: Written in snake_case for consistency.
- Semicolons: Always use semicolons (
;) at the end of statements. - File Endings: Always include an empty line at the end of files.
- Avoid Literal Strings in JSX: Always use
i18ntranslation files instead of hardcoded strings. - No Unused Variables: Always remove unused variables to keep the code clean.
- Environment Variables: Always define env variables inside the
envconfig file and import them withzodvalidation.