diff --git a/README.md b/README.md index 4a4e71705..ebc2239cd 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,14 @@ To create a new project using the boilerplate simply run : npx @react-native-community/cli@latest init MyApp --template @thecodingmachine/react-native-boilerplate ``` +During installation, you'll be asked: + +``` +📘 Using typescript ? (Y/n) +``` + +**Choose TypeScript (recommended)** or **Modern JavaScript (ESNext)** - the choice is yours! Both options provide the same architecture and features. + Assuming you have all the requirements installed, you can run the project by running: - `yarn start` to start the metro bundler, in a dedicated terminal diff --git a/documentation/blog/2026-02-26-React-Native-Boilerplate-5.0.0.md b/documentation/blog/2026-02-26-React-Native-Boilerplate-5.0.0.md new file mode 100644 index 000000000..32b8d1435 --- /dev/null +++ b/documentation/blog/2026-02-26-React-Native-Boilerplate-5.0.0.md @@ -0,0 +1,682 @@ +--- +title: React Native Boilerplate 5.0.0 +authors: jed +description: Major architecture refactoring and modern tooling +hide_table_of_contents: false +tags: [v5, boilerplate react, react-native, architecture, eslint, services] +--- + +The version 5.0.0 brings a major architecture refactoring focused on better code organization, +stricter project structure enforcement, and improved developer experience. +This update introduces a services layer, enforces consistent file naming conventions, +and provides powerful ESLint rules to maintain project structure integrity. + + + +## Migration Guide from 4.x to 5.0 + +### Breaking Changes Overview + +This version includes several breaking changes that require manual migration: + +1. **File naming convention** changed to kebab-case +2. **Services layer** introduced for business logic +3. **Navigator** renamed and relocated +4. **Test structure** reorganized +5. **Theme hooks** moved to dedicated location +6. **ESLint configuration** significantly enhanced + +### Step-by-Step Migration + +#### 1. File Naming Convention + +All component files now use kebab-case instead of PascalCase: + +**Before:** +``` +src/components/ + atoms/ + AssetByVariant/ + AssetByVariant.tsx + IconByVariant/ + IconByVariant.tsx + Skeleton/ + Skeleton.tsx +``` + +**After:** +``` +src/components/ + atoms/ + asset-by-variant/ + asset-by-variant.tsx + icon-by-variant/ + icon-by-variant.tsx + skeleton/ + skeleton.tsx +``` + +**Migration steps:** +- Rename all component folders to kebab-case +- Rename all component files to kebab-case +- Update all import statements accordingly + +The index files still use kebab-case and exports remain unchanged: + +```ts +// src/components/atoms/index.ts +export { default as AssetByVariant } from './asset-by-variant/asset-by-variant'; +export { default as IconByVariant } from './icon-by-variant/icon-by-variant'; +export { default as Skeleton } from './skeleton/skeleton'; +``` + +#### 2. App.tsx → app.tsx + +The main App file has been renamed to lowercase: + +**Before:** +```tsx +// src/App.tsx +import ApplicationNavigator from '@/navigation/Application'; + +function App() { + return ( + + + + + + + + ); +} +``` + +**After:** +```tsx +// src/app.tsx +import '@/services/i18n/instance'; +import { RootNavigator } from '@/navigators'; +import { queryClient } from './services/http-client'; +import { storage } from './services/storage'; + +function App() { + return ( + + + + + + + + ); +} +``` + +**Migration steps:** +- Rename `src/App.tsx` to `src/app.tsx` +- Update imports to use new services structure +- Update navigator import + +#### 3. Services Layer + +A new services layer has been introduced to centralize business logic, API calls, and configurations. + +##### HTTP Client + +**Before:** +```tsx +// src/App.tsx +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + mutations: { retry: false }, + }, +}); +``` + +**After:** +```ts +// src/services/http-client.ts +import { QueryClient } from '@tanstack/react-query'; +import ky from 'ky'; + +export const httpClient = ky.extend({ + headers: { Accept: 'application/json' }, + prefixUrl: `${process.env.API_URL ?? ''}/`, +}); + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { retry: false }, + mutations: { retry: false }, + }, +}); +``` + +##### Storage + +**Before:** +```tsx +// src/App.tsx +import { createMMKV } from 'react-native-mmkv'; +export const storage = createMMKV(); +``` + +**After:** +```ts +// src/services/storage.ts +import { createMMKV } from 'react-native-mmkv'; +export const storage = createMMKV(); +``` + +##### Domain Services + +The new domain-based service structure organizes API calls, schemas, and query options: + +``` +src/services/ + domains/ + user/ + user.api.ts # API calls + user.schema.ts # Zod schemas and types + user.query-options.ts # TanStack Query options +``` + +**Example:** + +```ts +// src/services/domains/user/user.schema.ts +import * as z from 'zod'; + +export const UserSchema = z.object({ + id: z.number(), + name: z.string(), +}); + +export type User = z.infer; +``` + +```ts +// src/services/domains/user/user.api.ts +import { httpClient } from '@/services/http-client'; +import { UserSchema } from './user.schema'; + +export const UserApis = { + fetchOne: async (id: number) => { + const response = await httpClient.get(`users/${id}`).json(); + return UserSchema.parse(response); + }, +}; +``` + +```ts +// src/services/domains/user/user.query-options.ts +import { queryOptions } from '@tanstack/react-query'; +import { UserApis } from './user.api'; +import type { User } from './user.schema'; + +export const UserQueryKeys = { + fetchOne: 'fetchOneUser', +}; + +export const fetchOneQueryOptions = (currentId: User['id']) => + queryOptions({ + enabled: currentId >= 0, + queryFn: () => UserApis.fetchOne(currentId), + queryKey: [UserQueryKeys.fetchOne, currentId], + }); +``` + +**Migration steps:** +- Move existing API calls to `services/domains/{domain}/{domain}.api.ts` +- Create schemas in `services/domains/{domain}/{domain}.schema.ts` +- Create query options in `services/domains/{domain}/{domain}.query-options.ts` +- Update imports throughout your codebase + +#### 4. I18n Refactoring + +Internationalization has been moved to the services layer: + +**Before:** +``` +src/ + translations/ + index.ts + en-EN.json + fr-FR.json + hooks/ + language/ + useI18n.ts + schema.ts +``` + +**After:** +``` +src/ + translations/ + en-en.json # lowercase + fr-fr.json # lowercase + services/ + i18n/ + instance.ts + i18next.d.ts +``` + +**Migration steps:** +- Rename translation files to lowercase (en-EN.json → en-en.json) +- Move i18n configuration to `services/i18n/instance.ts` +- Import i18n at the top of `app.tsx`: `import '@/services/i18n/instance';` +- Remove old `translations/index.ts` and `hooks/language/` files + +#### 5. Navigator Refactoring + +The navigator has been renamed and relocated: + +**Before:** +```tsx +// src/navigation/Application.tsx +function ApplicationNavigator() { + const { navigationTheme, variant } = useTheme(); + + return ( + + + + + + + + + ); +} +``` + +**After:** +```tsx +// src/navigators/root.tsx +function RootNavigator() { + const { navigationTheme, variant } = useTheme(); + + const { isError, isSuccess } = useQuery({ + queryFn: () => Promise.resolve(true), + queryKey: ['startup'], + }); + + return ( + + + + {isSuccess ? ( + + ) : ( + + )} + + + + ); +} +``` + +**Migration steps:** +- Move `src/navigation/Application.tsx` to `src/navigators/root.tsx` +- Rename `ApplicationNavigator` to `RootNavigator` +- Move navigation types to `src/services/navigation/types.ts` +- Move paths to `src/services/navigation/paths.ts` +- Update imports in your application + +#### 6. Hooks Reorganization + +**Before:** +``` +src/ + theme/ + hooks/ + useTheme.ts + hooks/ + domain/ + user/ + useUser.ts + userService.ts + schema.ts + language/ + useI18n.ts + schema.ts +``` + +**After:** +``` +src/ + hooks/ + use-theme/ + use-theme.ts + services/ + domains/ + user/ + user.api.ts + user.query-options.ts + user.schema.ts +``` + +**Migration steps:** +- Move `useTheme` to `hooks/use-theme/use-theme.ts` +- Move domain hooks to services layer +- Update import paths: `import { useTheme } from '@/hooks';` +- Convert domain hooks to use new query-options pattern + +#### 7. Theme Provider + +The ThemeProvider has been moved to components/providers: + +**Before:** +```tsx +import { ThemeProvider } from '@/theme'; +``` + +**After:** +```tsx +import { ThemeProvider } from '@/components/providers'; +``` + +#### 8. Test Structure + +Tests have been reorganized with a cleaner structure: + +**Before:** +``` +src/ + __mocks__/ + libs/ + react-native-reanimated.ts + tests/ + TestAppWrapper.tsx + components/ + atoms/ + Skeleton/ + Skeleton.test.tsx +``` + +**After:** +``` +src/ + __tests__/ + setup.ts + test-wrappers.tsx + mocks/ + get-assets-context.ts + libs/ + index.ts + react-native-reanimated.ts + react-native-safe-area-context.ts + react-native-mmkv.ts + components/ + atoms/ + skeleton/ + skeleton.test.tsx +``` + +**Migration steps:** +- Move `tests/TestAppWrapper.tsx` to `__tests__/test-wrappers.tsx` +- Create `__tests__/setup.ts` for test configuration +- Organize mocks in `__tests__/mocks/` +- Update jest.config.js: + +```js +module.exports = { + preset: 'react-native', + setupFilesAfterEnv: ['/src/__tests__/setup.ts'], + // ... rest of config +}; +``` + +- Update test imports: + +```tsx +// Before +import TestAppWrapper from '@/tests/TestAppWrapper'; + +// After +import TestAppWrapper from '@/__tests__/test-wrappers'; +``` + +#### 9. Screen Files + +Screens follow the same kebab-case convention: + +**Before:** +``` +src/screens/ + Example/ + Example.tsx + Example.test.tsx + Startup/ + Startup.tsx +``` + +**After:** +``` +src/screens/ + example/ + example.tsx + example.test.tsx + startup/ + startup.tsx +``` + +#### 10. Theme Assets + +Asset helper functions have been renamed: + +**Before:** +```tsx +import getAssetsContext from '@/theme/assets/getAssetsContext'; +``` + +**After:** +```tsx +import getAssetsContext from '@/theme/assets/get-assets-context'; +``` + +### Enhanced ESLint Configuration + +Version 5.0 introduces a powerful ESLint configuration that enforces project structure: + +#### New ESLint Plugins + +- **eslint-plugin-project-structure**: Enforces folder and file composition rules +- **eslint-plugin-perfectionist**: Ensures consistent sorting (imports, types, etc.) +- **eslint-plugin-unicorn**: Best practices and code quality +- **eslint-plugin-react-you-might-not-need-an-effect**: Prevents unnecessary effects + +#### Configuration Structure + +**Before:** +```js +// eslint.config.mjs +export default tseslint.config(/*...*/); +``` + +**After:** +```js +// eslint.config.js +import { fileCompositionConfig } from './eslint/file-composition/index.mjs'; +import { folderStructureConfig } from './eslint/folder-structure.mjs'; +import { independentModulesConfig } from './eslint/independent-modules.mjs'; + +export default defineConfig([ + { + files: ['**/*.{js,jsx,ts,tsx}'], + plugins: { 'project-structure': projectStructurePlugin }, + rules: { + 'project-structure/file-composition': [ERROR, fileCompositionConfig], + 'project-structure/folder-structure': [ERROR, folderStructureConfig], + 'project-structure/independent-modules': [ERROR, independentModulesConfig], + }, + }, + // ... other configs +]); +``` + +#### Project Structure Rules + +The new ESLint configuration enforces: + +1. **Folder Structure**: Ensures correct file locations +2. **File Composition**: Enforces file content patterns (hooks, components, etc.) +3. **Independent Modules**: Prevents circular dependencies and enforces module boundaries + +**Example folder structure rules:** + +```js +// eslint/folder-structure.mjs +export const folderStructureConfig = { + structure: { + children: [ + { + name: 'src', + children: [ + { name: 'components', children: [/* atoms, molecules, organisms, templates */] }, + { name: 'hooks' }, + { name: 'navigators' }, + { name: 'screens' }, + { name: 'services', children: [/* domains, i18n, navigation, storage */] }, + { name: 'theme' }, + { name: 'translations' }, + ], + }, + ], + }, +}; +``` + +**File composition example:** + +```js +// eslint/file-composition/hook.mjs +export const hookConfig = { + name: 'Hook', + rules: { + filePattern: '**/hooks/**/*.{ts,tsx}', + allowOnlySpecifiedSelectors: true, + selectors: ['function', 'exportNamedDeclaration'], + }, +}; +``` + +This ensures that hook files only contain function declarations and exports, preventing accidental complexity. + +### TypeScript Configuration Updates + +Minor improvements to tsconfig.json: + +```json +{ + "compilerOptions": { + "types": ["jest", "@testing-library/jest-native"] + } +} +``` + +### What Stays the Same + +- React Query for data fetching +- MMKV for storage +- Theme configuration approach +- Component architecture (atoms, molecules, organisms, templates) +- Testing with Jest and React Native Testing Library + +## New Features + +### Startup Logic with TanStack Query + +The RootNavigator now includes startup logic using TanStack Query: + +```tsx +const { isError, isSuccess } = useQuery({ + queryFn: () => Promise.resolve(true), + queryKey: ['startup'], +}); +``` + +This makes it easier to handle initialization logic and conditional rendering based on app state. + +### Ky HTTP Client + +The boilerplate now uses Ky as the HTTP client, providing a modern, lightweight alternative to axios: + +```ts +import ky from 'ky'; + +export const httpClient = ky.extend({ + headers: { Accept: 'application/json' }, + prefixUrl: `${process.env.API_URL ?? ''}/`, +}); +``` + +Ky provides: +- Better TypeScript support +- Simpler API +- Smaller bundle size +- Native Fetch API under the hood + +### Domain-Driven Architecture + +The new services layer encourages domain-driven design: + +``` +services/ + domains/ + user/ # User domain + product/ # Product domain + auth/ # Auth domain +``` + +Each domain contains: +- **schema**: Type definitions and validation +- **api**: API calls +- **query-options**: TanStack Query configuration + +This makes the codebase more maintainable and testable. + +## Migration Checklist + +Use this checklist to ensure a complete migration: + +- [ ] Rename all component files and folders to kebab-case +- [ ] Rename App.tsx to app.tsx +- [ ] Create services layer structure +- [ ] Move queryClient to services/http-client.ts +- [ ] Move storage to services/storage.ts +- [ ] Move i18n to services/i18n/ +- [ ] Rename translation files to lowercase +- [ ] Move navigator to navigators/root.tsx +- [ ] Rename ApplicationNavigator to RootNavigator +- [ ] Move navigation types to services/navigation/types.ts +- [ ] Move navigation paths to services/navigation/paths.ts +- [ ] Move useTheme to hooks/use-theme/use-theme.ts +- [ ] Update ThemeProvider import +- [ ] Reorganize tests to __tests__ structure +- [ ] Update jest.config.js setupFilesAfterEnv +- [ ] Migrate domain hooks to services structure +- [ ] Update all import paths throughout the codebase +- [ ] Update eslint.config.js (or use the new configuration) +- [ ] Run ESLint and fix any issues: `npm run lint` +- [ ] Run tests: `npm test` +- [ ] Rename all screen files to kebab-case +- [ ] Update getAssetsContext imports + +## Conclusion + +Version 5.0 brings significant architectural improvements that will make your codebase more maintainable, +testable, and scalable. While the migration requires some effort, the benefits of better structure, +stricter linting, and clearer separation of concerns will pay dividends in the long run. + +The new services layer, domain-driven architecture, and powerful ESLint rules ensure that your project +stays organized as it grows. + +Happy coding! 🚀 diff --git a/documentation/docs/01-Getting Started.mdx b/documentation/docs/01-Getting Started.mdx index 01a3c23b6..12df40f57 100644 --- a/documentation/docs/01-Getting Started.mdx +++ b/documentation/docs/01-Getting Started.mdx @@ -23,17 +23,35 @@ all while maintaining a clear separation of concerns. If you find value in this boilerplate, consider giving us a star. It would brighten our day like a ray of sunshine! 🌈☀️ ::: +## TypeScript or JavaScript? You Choose! 🎯 + +One of the most frequently requested features: **the boilerplate works with both TypeScript and Modern JavaScript (ESNext)!** + +During installation, you'll be prompted to choose: +```bash +📘 Using typescript ? (Y/n) +``` + +- **TypeScript (Default)**: Full type safety, enhanced IDE support, and better refactoring capabilities +- **Modern JavaScript (ESNext/ES2022)**: Latest JavaScript features and syntax, automatically compiled from the TypeScript source + +Both options provide the **exact same architecture, features, and code organization**. The only difference is the language syntax. + +[Learn more about the installation process and language choice →](/docs/installation#using-the-boilerplate) + ## Features | Features | Description | | ---------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [`Javascript or TypeScript`](/docs/installation#using-the-boilerplate) | Every project, developer, team, and experience is unique. That's why you have the freedom to select either a JavaScript or TypeScript codebase. [The choice is yours!](/docs/installation#using-the-boilerplate) | +| [`Javascript or TypeScript`](/docs/installation#using-the-boilerplate) | Every project, developer, team, and experience is unique. That's why you have the freedom to select either a Modern JavaScript (ESNext) or TypeScript codebase. [The choice is yours!](/docs/installation#using-the-boilerplate) | | [`Navigation`](/docs/navigate) | With [React Navigation](https://reactnavigation.org/), we offer a swift start to your navigation structure through a robust dependency. Check out the details in our [navigation structure documentation](/docs/navigate#navigation-structure). | -| [`Data fetching`](/docs/data-fetching)️ | Leveraging [TanStackQuery](https://react-query.tanstack.com/), data fetching has never been this effortless. | +| [`Data fetching`](/docs/data-fetching)️ | Leveraging [TanStack Query](https://tanstack.com/query/latest), data fetching has never been this effortless, with a clean domain-based architecture. | | [`Internationalization`](/docs/internationalization) | Our application is fully prepared for multilingual support, all thanks to [React i18next](https://react.i18next.com/). | | [`Multi theming`](/docs/theming/how-to-use) | Without any extra dependencies, we offer an easy-to-use and maintainable theme configuration | -| [`Safe synchrone storage`](/docs/storage) | With [React Native MMKV](https://github.com/mrousavy/react-native-mmkv), storing data becomes a breeze, and it can be done securely. | +| [`Safe synchronous storage`](/docs/storage) | With [React Native MMKV](https://github.com/mrousavy/react-native-mmkv), storing data becomes a breeze, and it can be done securely. | | [`Environment`](/docs/environment) | The app comes pre-installed with all the necessary tools for handling simple environment variables | +| [`Project Structure Enforcement`](/docs/eslint-project-structure) | Powerful ESLint rules ensure consistent architecture, preventing common mistakes and enforcing best practices automatically. | +| [`Testing`](/docs/testing) | Pre-configured [Jest](https://jestjs.io/) and [React Native Testing Library](https://callstack.github.io/react-native-testing-library/) setup with organized mocks and test utilities. | ## Dependencies diff --git a/documentation/docs/02-Installation.md b/documentation/docs/02-Installation.md index dd3117be0..eb51eb863 100644 --- a/documentation/docs/02-Installation.md +++ b/documentation/docs/02-Installation.md @@ -26,8 +26,43 @@ During the installation process, you will encounter the following prompt: 📘 Using typescript ? (Y/n) ``` -This prompt gives you the flexibility to choose whether you want to use TypeScript or not. -If you choose not to use TypeScript, the project will be created without any TypeScript configuration. +This prompt gives you the flexibility to choose whether you want to use TypeScript or JavaScript for your project. + +### TypeScript (Default - Recommended) + +If you choose **Yes** (or simply press Enter), your project will be set up with TypeScript. This includes: +- Full TypeScript support with strict type checking +- Type definitions for all services, components, and hooks +- Enhanced IDE autocomplete and error detection +- Better refactoring capabilities + +### JavaScript + +If you choose **No**, the boilerplate will automatically: +1. 📦 Install TypeScript temporarily as a build tool +2. 🧱 Compile all TypeScript source files to JavaScript +3. 🖼️ Copy assets (images, icons) to the compiled output +4. ♻️ Replace the TypeScript source with the compiled JavaScript code +5. 🌀 Remove TypeScript-specific files: + - Type definition files (`*.d.ts`) + - Type directories (`src/services/theme-generation/types/`) + - Navigation type files + +The resulting project will be pure JavaScript while maintaining the same architecture and structure. + +:::tip Windows Users +If you're on Windows, we recommend using **Git Bash** or **WSL** for the installation process. The JavaScript compilation uses POSIX shell commands that are not available in Command Prompt or PowerShell. + +If you encounter `spawnSync ENOENT` errors, ensure you're using Git Bash or check the [FAQ troubleshooting section](/docs/faq#installation--troubleshooting). +::: + +:::note ESLint Configuration +The ESLint configuration is designed to work with both TypeScript and JavaScript projects. When using JavaScript, TypeScript-specific rules are automatically disabled. +::: + +:::warning Migration Limitation +Once you choose JavaScript, migrating back to TypeScript requires manual effort. Consider your team's preferences carefully before making this choice. +::: ## Running the project diff --git a/documentation/docs/03-Project Structure.md b/documentation/docs/03-Project Structure.md index bf38f3755..7ca1086b4 100644 --- a/documentation/docs/03-Project Structure.md +++ b/documentation/docs/03-Project Structure.md @@ -16,11 +16,12 @@ To achieve this, the project structure is thoughtfully organized into distinct s |--------------------|-------------------------------------------------------------------------------------------------------------------| | `src/components` | Home to application components, following the atomic design methodology for organizing presentational components. | | `src/hooks` | Custom hooks used throughout the application. | -| `src/navigation` | Navigator components responsible for handling navigation. | +| `src/navigators` | Navigator components responsible for handling navigation. | | `src/screens` | Screen components representing various app screens. | -| `src/services` ️ | Houses data fetching and related services. | +| `src/services` | Houses data fetching, HTTP client, i18n, navigation types, and business logic organized by domain. | | `src/theme` | Holds theme configuration for the application. | -| `src/translations` | Configuration related to language support. | +| `src/translations` | Translation files for language support (e.g., `en-en.json`, `fr-fr.json`). | +| `src/__tests__` | Test configuration, mocks, and test utilities. | ## Specific Top-Level Boilerplate Files @@ -28,12 +29,71 @@ To achieve this, the project structure is thoughtfully organized into distinct s |--------------------|-----------------------------------------------------| | `.env` | Environment variables configuration. | | `jest.config.js` | Configuration file for Jest testing. | -| `jest.setup.js` | Jest mocking configuration. | | `tsconfig.json` | TypeScript configuration (for TypeScript projects). | -| `src/App.{js.tsx}` | Main component of the application. | +| `src/app.tsx` | Main component of the application. | +| `eslint.config.mjs` | ESLint configuration with project structure rules. | ## Atomic Design The `components` folder follows the [atomic design](https://bradfrost.com/blog/post/atomic-web-design/) methodology. This approach emphasizes modularity and reusability by breaking down elements into atomic components. By doing so, development teams can create more consistent, scalable, and maintainable projects. + +## Services Layer + +The `services` folder is organized to separate business logic, data fetching, and application configuration: + +### Domain-Based Organization + +``` +src/services/ + domains/ + user/ + user.api.ts # API calls + user.schema.ts # Zod schemas and types + user.query-options.ts # TanStack Query options + product/ + product.api.ts + product.schema.ts + product.query-options.ts +``` + +Each domain contains: +- **`*.api.ts`**: API calls using the HTTP client +- **`*.schema.ts`**: Zod schemas for validation and TypeScript types +- **`*.query-options.ts`**: TanStack Query configuration (query keys, options) + +### Core Services + +| File | Description | +|---------------------------|------------------------------------------------------------------| +| `http-client.ts` | Ky HTTP client instance and TanStack Query client configuration. | +| `storage.ts` | MMKV storage instance for persistent data. | +| `i18n/instance.ts` | i18next configuration and language management. | +| `i18n/i18next.d.ts` | TypeScript type definitions for translations. | +| `navigation/types.ts` | Navigation types and parameter lists. | +| `navigation/paths.ts` | Navigation path constants. | + +This structure promotes: +- **Separation of concerns**: Each file has a single responsibility +- **Type safety**: Schemas provide runtime validation and compile-time types +- **Testability**: Easy to mock and test individual services +- **Scalability**: New domains can be added without affecting existing code + +## File Naming Convention + +All files and folders use **kebab-case** naming convention: + +``` +✅ Correct: + - asset-by-variant.tsx + - use-theme.ts + - user.api.ts + +❌ Incorrect: + - AssetByVariant.tsx + - useTheme.ts + - UserAPI.ts +``` + +This ensures consistency across the codebase and is enforced by ESLint rules. diff --git a/documentation/docs/04-Guides/01-Navigate.md b/documentation/docs/04-Guides/01-Navigate.md index 870d65bdd..18cd7cd9e 100644 --- a/documentation/docs/04-Guides/01-Navigate.md +++ b/documentation/docs/04-Guides/01-Navigate.md @@ -13,16 +13,24 @@ any new features and improvements. ## Navigation structure -All navigation-related configurations and navigators are neatly organized within the `src/navigation` folder. Here's a brief overview: +All navigation-related configurations and navigators are neatly organized within the `src/navigators` folder. Here's a brief overview: -### Root file (`Application.{js, tsx}`) +### Root file (`root.tsx`) This serves as the root navigator, which is responsible for handling the initial navigation of the application. -It's a simple stack navigator that includes the [`Startup`](/docs/data-fetching#fetching-data-at-startup) screen and an Example screen. +It's a simple stack navigator that includes a `Startup` screen and an `Example` screen with conditional rendering based on startup state. -The workflow is designed so that when the application launches, the user is initially presented with the `Startup` screen. -This screen takes on the responsibility of loading essential application data, such as user profiles and settings. -Once this data is loaded, the `Startup` screen facilitates navigation to the `Example` screen. +The workflow is designed so that when the application launches, the user is initially presented with the `Startup` screen or the `Example` screen depending on the result of the startup query. +The navigator uses TanStack Query to handle initialization logic: + +```tsx +const { isError, isSuccess } = useQuery({ + queryFn: () => Promise.resolve(true), + queryKey: ['startup'], +}); +``` + +Once the startup query succeeds, users are navigated to the `Example` screen. This pattern makes it easy to handle loading states, errors, and conditional navigation. As your application evolves, you have the flexibility to extend this file by adding more screens and navigators. @@ -34,7 +42,9 @@ You can either add your own navigators or, if you prefer, replace the existing s ## Using typescript It's crucial not to overlook the creation of types for your navigation parameters. This practice helps prevent errors and enhances autocompletion. -You can define these types in the `@/navigation/types.ts` file. +You can define these types in the `src/services/navigation/types.ts` file. + +Navigation paths are defined in `src/services/navigation/paths.ts` for centralized path management. For more in-depth information on this topic, please refer to the [React Navigation documentation](https://reactnavigation.org/docs/typescript/). diff --git a/documentation/docs/04-Guides/02-Data Fetching.md b/documentation/docs/04-Guides/02-Data Fetching.md index e2c4602ab..6e9fbf675 100644 --- a/documentation/docs/04-Guides/02-Data Fetching.md +++ b/documentation/docs/04-Guides/02-Data Fetching.md @@ -14,87 +14,152 @@ greatly enhance the efficiency and reliability of data management in your applic ## Fetching Data at Startup This boilerplate offers a convenient method for fetching data before presenting the application content to the user. -This capability is made possible through the [navigation structure](/docs/navigate#navigation-structure) of the initial -setup and the inclusion of the `Startup` screen. +This capability is made possible through the [navigation structure](/docs/navigate#navigation-structure) where the +`RootNavigator` conditionally renders screens based on startup state. -The `Startup` screen takes on the role of being the very first screen shown to the user upon launching the application. -It serves as the entry point where essential data can be fetched and prepared before the user interacts with the app's content. -This ensures a smoother and more responsive user experience. +The `RootNavigator` uses TanStack Query to handle initialization logic: -```tsx title="src/screens/Startup/Startup.tsx" - // highlight-next-line +```tsx title="src/navigators/root.tsx" import { useQuery } from '@tanstack/react-query'; -const Startup = ({ navigation }: ApplicationScreenProps) => { - const { layout, gutters, fonts } = useTheme(); - const { t } = useTranslation(); - - // highlight-start - const { isSuccess, isFetching, isError } = useQuery({ - queryKey: ['startup'], - queryFn: () => { - // Fetch data here - return Promise.resolve(true); - }, - }); - // highlight-end - - useEffect(() => { - navigation.reset({ - index: 0, - routes: [{ name: 'Main' }], - }); - }, [isSuccess]); - - return ( - //... - ); +function RootNavigator() { + const { navigationTheme, variant } = useTheme(); + + // highlight-start + const { isError, isSuccess } = useQuery({ + queryFn: () => { + // Fetch startup data here + return Promise.resolve(true); + }, + queryKey: ['startup'], + }); + // highlight-end + + return ( + + + + {/* highlight-start */} + {isSuccess ? ( + + ) : ( + + )} + {/* highlight-end */} + + + + ); +} +``` + +This approach ensures a cleaner separation of concerns: the navigator handles navigation logic while screens focus on UI and user interaction. + +## Domain-Based Data Fetching + +The boilerplate uses a domain-based architecture for organizing data fetching logic. Each domain contains: + +### 1. Schema Definition + +Define your data types and validation using Zod: + +```ts title="src/services/domains/user/user.schema.ts" +import * as z from 'zod'; + +export const UserSchema = z.object({ + id: z.number(), + name: z.string(), + email: z.string().email(), +}); + +export type User = z.infer; +``` + +### 2. API Calls + +Create API functions using the HTTP client: + +```ts title="src/services/domains/user/user.api.ts" +import { httpClient } from '@/services/http-client'; +import { UserSchema } from './user.schema'; + +export const UserApis = { + fetchOne: async (id: number) => { + const response = await httpClient.get(`users/${id}`).json(); + return UserSchema.parse(response); + }, + + fetchAll: async () => { + const response = await httpClient.get('users').json(); + return z.array(UserSchema).parse(response); + }, }; ``` -The `useQuery` hook is employed for data fetching. Now, let's explore how to formulate the request. +The `httpClient` is a pre-configured [Ky](https://github.com/sindresorhus/ky) instance located in `src/services/http-client.ts`. + +### 3. Query Options -Consider a scenario where we wish to retrieve application settings from an API before the user accesses the application's content. -To achieve this, we will create a service responsible for fetching this data. +Define TanStack Query configuration: -```ts -import { instance } from '@/services/instance'; +```ts title="src/services/domains/user/user.query-options.ts" +import { queryOptions } from '@tanstack/react-query'; +import { UserApis } from './user.api'; +import type { User } from './user.schema'; -export default async () => instance.get(`/settings`); +export const UserQueryKeys = { + fetchOne: 'fetchOneUser', + fetchAll: 'fetchAllUsers', +}; + +export const fetchOneUserQueryOptions = (userId: User['id']) => + queryOptions({ + enabled: userId >= 0, + queryFn: () => UserApis.fetchOne(userId), + queryKey: [UserQueryKeys.fetchOne, userId], + }); + +export const fetchAllUsersQueryOptions = () => + queryOptions({ + queryFn: () => UserApis.fetchAll(), + queryKey: [UserQueryKeys.fetchAll], + }); ``` -The `instance` is an http client instance that comes pre-configured in the boilerplate. +### 4. Using in Components -Next, we will use the `fetchSettings` service within the `Startup` screen. +Import and use the query options in your components: -```tsx title="src/screens/Startup/Startup.tsx" +```tsx title="src/screens/example/example.tsx" import { useQuery } from '@tanstack/react-query'; -// highlight-next-line -import fetchSettings from '@/folder/fetchSettings'; - -const Startup = ({ navigation }: ApplicationScreenProps) => { - const { layout, gutters, fonts } = useTheme(); - const { t } = useTranslation(['startup']); - - const { isSuccess, isFetching, isError } = useQuery({ - queryKey: ['startup'], - // highlight-next-line - queryFn: fetchSettings, - }); - - useEffect(() => { - navigation.reset({ - index: 0, - routes: [{ name: 'Main' }], - }); - }, [isSuccess]); - - return ( - //... - ); -}; +import { fetchOneUserQueryOptions } from '@/services/domains/user/user.query-options'; + +function ExampleScreen() { + const userId = 1; + + const { data: user, isLoading, isError } = useQuery( + fetchOneUserQueryOptions(userId) + ); + + if (isLoading) return Loading...; + if (isError) return Error loading user; + + return {user.name}; +} ``` +## Benefits of This Architecture + +- **Type Safety**: Zod schemas provide runtime validation and compile-time types +- **Centralized Logic**: All domain logic is in one place +- **Reusability**: Query options can be reused across components +- **Testability**: Each layer can be tested independently +- **Consistency**: Enforced by ESLint rules for project structure + ## Advanced usage Since we've utilized no additional or custom configuration, for further information, diff --git a/documentation/docs/04-Guides/03-I18n.md b/documentation/docs/04-Guides/03-I18n.md index 275d808f0..72ebc3174 100644 --- a/documentation/docs/04-Guides/03-I18n.md +++ b/documentation/docs/04-Guides/03-I18n.md @@ -18,9 +18,76 @@ within the application's interface. ## Translation files structure All the translations are situated in the `src/translations` folder. -Within this folder, you'll find one file for each language. +Within this folder, you'll find one file for each language, following the kebab-case naming convention: + +``` +src/translations/ + en-en.json + fr-fr.json +``` + +These translation files are loaded into the i18n instance, which is managed in the `src/services/i18n/` folder: + +``` +src/services/i18n/ + instance.ts # i18next configuration and initialization + i18next.d.ts # TypeScript type definitions for translations +``` + +The i18n instance is initialized when importing the application: + +```tsx title="src/app.tsx" +import '@/services/i18n/instance'; +``` + +## Type-Safe Translations + +The boilerplate provides full TypeScript support for translations. The `i18next.d.ts` file extends i18next types +to provide autocomplete and type checking for translation keys: + +```ts title="src/services/i18n/i18next.d.ts" +import type { defaultNS, resources, SupportedLanguages } from '@/services/i18n/instance'; + +declare module 'i18next' { + type CustomTypeOptions = { + defaultNS: typeof defaultNS; + resources: defaultTranslations; + }; + + interface i18n { + changeLanguage(lng: SupportedLanguages, callback?: Callback): Promise; + language: SupportedLanguages; + } +} +``` + +This ensures that: +- Translation keys are validated at compile-time +- You get autocomplete when using `t()` function +- Language changes are type-safe + +## Supported Languages + +Languages are defined as an enum in the i18n instance: + +```ts title="src/services/i18n/instance.ts" +export const enum SupportedLanguages { + EN_EN = 'en-en', + FR_FR = 'fr-fr', +} + +export const languageSchema = z.enum([ + SupportedLanguages.EN_EN, + SupportedLanguages.FR_FR, +]); +``` + +To add a new language: +1. Add a new translation file (e.g., `es-es.json`) +2. Import it in `instance.ts` +3. Add it to the `SupportedLanguages` enum +4. Add it to the `languageSchema` -These translation files are loaded into the i18n instance, which is located in the `src/translations/index.ts` file. This setup centralizes and manages the translation resources for your application, making it easier to maintain and switch between different languages as needed. diff --git a/documentation/docs/04-Guides/04-Theming/01-Using.md b/documentation/docs/04-Guides/04-Theming/01-Using.md index 515590843..a9c21efd6 100644 --- a/documentation/docs/04-Guides/04-Theming/01-Using.md +++ b/documentation/docs/04-Guides/04-Theming/01-Using.md @@ -6,9 +6,9 @@ id: theming-how-to-use keywords: [theme, theming, useTheme, hooks, themeProvider] --- -The boilerplate provides a pre-configured theme ready for use. -To make use of it, simply follow this section's instructions. -If you'd like to gain a deeper understanding of the theme configuration, please refer to +The boilerplate provides a pre-configured theme ready for use. +To make use of it, simply follow this section's instructions. +If you'd like to gain a deeper understanding of the theme configuration, please refer to the [configuration](/docs/theming/configuration) section for more details. ## `useTheme` hook @@ -16,7 +16,7 @@ the [configuration](/docs/theming/configuration) section for more details. If you need to access the style classes, you can use the `useTheme` hook in the following manner: ```tsx -import { useTheme } from '@/theme'; +import { useTheme } from '@/hooks'; const Example = () => { const { @@ -32,7 +32,7 @@ const Example = () => { components, //highlight-end } = useTheme(); - + return ( { {isError && ( @@ -61,18 +61,18 @@ const Example = () => { ## Change theme -As mentioned in the [configuration](/docs/theming/configuration) section, you have the flexibility to add new themes +As mentioned in the [configuration](/docs/theming/configuration) section, you have the flexibility to add new themes (variants) and switch between them directly within the app. For example, if you have a `default` theme configured with a `dark` variant, you can switch the current theme to `dark` at any point. To achieve this, you can employ the `useTheme` hook as follows: ```tsx -import { useTheme } from '@/theme'; +import { useTheme } from '@/hooks'; const Example = () => { const { changeTheme } = useTheme(); - + return (