{"id":26976113,"url":"https://github.com/nuhptr/react-router-v7-learn","last_synced_at":"2025-04-03T11:35:48.552Z","repository":{"id":283916097,"uuid":"953282734","full_name":"nuhptr/react-router-v7-learn","owner":"nuhptr","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-23T01:36:15.000Z","size":0,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-23T02:26:59.975Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nuhptr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-23T01:35:50.000Z","updated_at":"2025-03-23T01:36:18.000Z","dependencies_parsed_at":"2025-03-23T03:15:27.858Z","dependency_job_id":null,"html_url":"https://github.com/nuhptr/react-router-v7-learn","commit_stats":null,"previous_names":["nuhptr/react-router-v7-learn"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuhptr%2Freact-router-v7-learn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuhptr%2Freact-router-v7-learn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuhptr%2Freact-router-v7-learn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nuhptr%2Freact-router-v7-learn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nuhptr","download_url":"https://codeload.github.com/nuhptr/react-router-v7-learn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246995668,"owners_count":20866449,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-03T11:35:47.780Z","updated_at":"2025-04-03T11:35:48.544Z","avatar_url":"https://github.com/nuhptr.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Table of Contents\n\n- [Table of Contents](#table-of-contents)\n  - [Installation](#installation)\n  - [Routing](#routing)\n  - [Route Module](#route-module)\n  - [Rendering Strategies](#rendering-strategies)\n  - [Data Loading](#data-loading)\n  - [Actions](#actions)\n  - [Navigating](#navigating)\n  - [Pending UI](#pending-ui)\n  - [Testing](#testing)\n  - [Custom Framework](#custom-framework)\n  - [Conclusion](#conclusion)\n\n---\n\n## Installation\n\nMost React Router projects start with a template. These templates are maintained by the React Router team and help you quickly get set up:\n\n```bash\nnpx create-react-router@latest my-react-router-app\n```\n\nNow change into the new directory and start the development server:\n\n```bash\ncd my-react-router-app\nnpm i\nnpm run dev\n```\n\nOpen your browser to `http://localhost:5173` to see the running app.\n\nFor more templates and deployment-ready setups, refer to the official React Router templates.\n\n---\n\n## Routing\n\n**Where do routes live?**\nRoutes are defined in `app/routes.ts`. Each route has a URL pattern and a file path that points to its route module (the file defining its logic and UI).\n\n**Basic Example:**\n\n```typescript\nimport { type RouteConfig, route } from \"@react-router/dev/routes\"\n\nexport default [route(\"some/path\", \"./some/file.tsx\")] satisfies RouteConfig\n```\n\n**More Complex Example:**\n\n```typescript\nimport { type RouteConfig, route, index, layout, prefix } from \"@react-router/dev/routes\"\n\nexport default [\n    index(\"./home.tsx\"),\n    route(\"about\", \"./about.tsx\"),\n\n    layout(\"./auth/layout.tsx\", [route(\"login\", \"./auth/login.tsx\"), route(\"register\", \"./auth/register.tsx\")]),\n\n    ...prefix(\"concerts\", [\n        index(\"./concerts/home.tsx\"),\n        route(\":city\", \"./concerts/city.tsx\"),\n        route(\"trending\", \"./concerts/trending.tsx\"),\n    ]),\n] satisfies RouteConfig\n```\n\nYou can also use file-system routing conventions by using `@react-router/fs-routes`.\n\n**Nested Routes:**\n\n```typescript\nexport default [route(\"dashboard\", \"./dashboard.tsx\", [index(\"./home.tsx\"), route(\"settings\", \"./settings.tsx\")])]\n```\n\nThe parent route’s `\u003cOutlet /\u003e` renders the child routes.\n\n**Root Route:**\nEvery route is nested inside `app/root.tsx`. This special root route can provide global layouts or error boundaries.\n\n**Layout Routes:**\nLayout routes create nesting in the UI without adding to the URL.\n\n**Index Routes:**\n`index()` defines a default child route at the parent’s URL.\n\n**Route Prefixes:**\n`prefix()` can add a path prefix to a set of routes for organization.\n\n**Dynamic Segments:**\nUse `:paramName` in the path to define a dynamic URL segment. These values are available in `params` in loaders, actions, and components.\n\n---\n\n## Route Module\n\nEach route’s file (referenced in `routes.ts`) is a \"route module.\" It can contain:\n\n-   A default exported component to render the UI.\n-   `loader` or `clientLoader` for data fetching.\n-   `action` or `clientAction` for data mutations.\n-   Error boundaries and headers.\n\n**Example:**\n\n```typescript\nimport type { Route } from \"./+types/team\"\n\nexport async function loader({ params }: Route.LoaderArgs) {\n    let team = await fetchTeam(params.teamId)\n    return { name: team.name }\n}\n\nexport default function Team({ loaderData }: Route.ComponentProps) {\n    return \u003ch1\u003e{loaderData.name}\u003c/h1\u003e\n}\n```\n\nNested routes let you build complex layouts. Remember to place `\u003cOutlet /\u003e` in the parent route’s component to render children.\n\n---\n\n## Rendering Strategies\n\nReact Router supports three main strategies:\n\n1. **Client Side Rendering (CSR):**\n   The entire app runs in the browser.\n\n    ```typescript\n    import type { Config } from \"@react-router/dev/config\"\n    export default {\n        ssr: false,\n    } satisfies Config\n    ```\n\n2. **Server Side Rendering (SSR):**\n   The initial render happens on the server.\n\n    ```typescript\n    export default {\n        ssr: true,\n    } satisfies Config\n    ```\n\n3. **Static Pre-rendering:**\n   Generate static HTML at build time.\n    ```typescript\n    export default {\n        async prerender() {\n            return [\"/\", \"/about\", \"/contact\"]\n        },\n    } satisfies Config\n    ```\n\nThese strategies can be mixed. For example, you can statically pre-render some URLs and server-render others.\n\n---\n\n## Data Loading\n\nData is fetched in route modules using `loader` and `clientLoader`.\n\n-   **clientLoader:** Fetch data in the browser only.\n-   **loader:** Fetch data on the server (for SSR) or at build time (for pre-rendering).\n\n**Client Data Loading Example:**\n\n```typescript\nexport async function clientLoader({ params }: Route.ClientLoaderArgs) {\n    const res = await fetch(`/api/products/${params.pid}`)\n    return await res.json()\n}\n```\n\n**Server Data Loading Example:**\n\n```typescript\nexport async function loader({ params }: Route.LoaderArgs) {\n    return fakeDb.getProduct(params.pid)\n}\n```\n\nYou can use both `loader` and `clientLoader` in the same route: `loader` for SSR/pre-rendered content, and `clientLoader` for subsequent navigations in the browser.\n\n---\n\n## Actions\n\nActions handle data mutations (POST, PUT, DELETE). After completion, all loader data is revalidated automatically.\n\n-   **clientAction:** Runs in the browser.\n-   **action:** Runs on the server.\n\n**Client Action Example:**\n\n```typescript\nexport async function clientAction({ request }: Route.ClientActionArgs) {\n    let formData = await request.formData()\n    let title = formData.get(\"title\")\n    let project = await someApi.updateProject({ title })\n    return project\n}\n```\n\n**Calling Actions:**\n\n-   Using `\u003cForm method=\"post\"\u003e`\n-   Using `useSubmit()` hook\n-   Using `\u003cfetcher.Form\u003e` for no-navigation updates\n\n---\n\n## Navigating\n\nUsers navigate using:\n\n-   **`\u003cNavLink\u003e`:** For styled navigation with active/pending states.\n-   **`\u003cLink\u003e`:** Simple navigation.\n-   **`\u003cForm\u003e`:** Submit data and navigate based on user input.\n-   **`redirect`** inside loaders/actions.\n-   **`useNavigate()`:** Imperative navigation hook.\n\n**Example `\u003cNavLink\u003e`:**\n\n```jsx\n\u003cNavLink to=\"/home\" className={({ isActive }) =\u003e (isActive ? \"active\" : \"\")}\u003e\n    Home\n\u003c/NavLink\u003e\n```\n\n---\n\n## Pending UI\n\nShow pending states while data loads or actions run:\n\n-   `useNavigation()` gives global navigation states.\n-   `\u003cNavLink\u003e` can display pending states.\n-   `\u003cfetcher\u003e` forms show localized pending states.\n\n**Fetcher Example:**\n\n```jsx\nconst fetcher = useFetcher()\nreturn (\n    \u003cfetcher.Form method=\"post\"\u003e\n        \u003cbutton type=\"submit\"\u003e{fetcher.state !== \"idle\" ? \"Saving...\" : \"Save\"}\u003c/button\u003e\n    \u003c/fetcher.Form\u003e\n)\n```\n\n**Optimistic UI:**\nUse the submitted data to immediately update the UI, even before the server responds, for a more responsive feel.\n\n---\n\n## Testing\n\nUse `createRoutesStub` to test components that rely on React Router’s context:\n\n```jsx\nimport { createRoutesStub } from \"react-router\"\nimport { render, screen } from \"@testing-library/react\"\nimport { LoginForm } from \"./LoginForm\"\n\nconst Stub = createRoutesStub([\n    {\n        path: \"/login\",\n        Component: LoginForm,\n        action() {\n            return { errors: { username: \"Username required\" } }\n        },\n    },\n])\n\ntest(\"shows error messages\", async () =\u003e {\n    render(\u003cStub initialEntries={[\"/login\"]} /\u003e)\n    // interact and assert...\n})\n```\n\nThis allows you to test route modules and components in isolation, ensuring your UI logic is solid.\n\n---\n\n## Custom Framework\n\nPutting it all together, React Router behaves like a full framework:\n\n-   File-based (or configuration-based) routing\n-   Nested routes for layouts\n-   Data loading and actions integrated at the route level\n-   SSR, CSR, and static pre-rendering all supported\n-   Automatic data revalidation after actions\n-   Pending states, optimistic UI, and fetchers for a rich user experience\n\nThis cohesive approach reduces boilerplate, clarifies data flow, and makes building scalable, data-driven React apps much simpler.\n\n---\n\n## Conclusion\n\nBy following these steps, you’ve learned how to install React Router, configure routes, define route modules, choose rendering strategies, load data, perform actions, navigate efficiently, handle pending UI states, test route modules, and leverage React Router’s tools as a complete framework.\n\nHappy routing!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnuhptr%2Freact-router-v7-learn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnuhptr%2Freact-router-v7-learn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnuhptr%2Freact-router-v7-learn/lists"}