Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/oedotme/generouted
Generated file-based routes for Vite
https://github.com/oedotme/generouted
actions code-splitting data-loaders file-based-routing generate nested-layouts nextjs pages pre-loading react react-location react-router react-router-dom remix router routes solid solid-router typescript vite
Last synced: 23 days ago
JSON representation
Generated file-based routes for Vite
- Host: GitHub
- URL: https://github.com/oedotme/generouted
- Owner: oedotme
- License: mit
- Created: 2022-04-08T11:59:02.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-09-10T10:29:05.000Z (about 2 months ago)
- Last Synced: 2024-09-30T23:40:55.098Z (about 1 month ago)
- Topics: actions, code-splitting, data-loaders, file-based-routing, generate, nested-layouts, nextjs, pages, pre-loading, react, react-location, react-router, react-router-dom, remix, router, routes, solid, solid-router, typescript, vite
- Language: TypeScript
- Homepage: https://stackblitz.com/github.com/oedotme/generouted/tree/main/explorer
- Size: 1.37 MB
- Stars: 1,027
- Watchers: 9
- Forks: 48
- Open Issues: 4
-
Metadata Files:
- Readme: readme.md
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-vite - generouted - Client-side type-safe file-based routing and global modals โ supports layouts, loaders, code-splitting and more. (Plugins / React)
- jimsghstars - oedotme/generouted - Generated file-based routes for Vite (TypeScript)
- awesome-vite - generouted - Client-side type-safe file-based routing and global modals โ supports layouts, loaders, code-splitting and more. (Plugins / React)
README
# Generouted
Generated file-based routes for [Vite](https://vitejs.dev)
Motivation
I enjoyed using file-based routing since I tried Next.js (pages directory). After applying the same concept with Vite and client-side applications, I started writing blog posts covering the implementation of [client-side file-based routing with React Router](https://omarelhawary.me/blog/file-based-routing-with-react-router) which was packaged later as `generouted`.
Today `generouted` brings many features, supports multiple frameworks and routers, and inspires ideas and conventions from Next.js, Remix, Expo, Docusaurus, SvelteKit and more.
How does it work?
`generouted` uses [Vite's glob import API](https://vitejs.dev/guide/features.html#glob-import) to list the routes within the `src/pages` directory and generates the routes tree and modals based on `generouted`'s conventions.
There are also Vite plugins available for some integrations to provide type-safe components/hooks/utils through code-generation.
## Features
- ๐ Client-side file-based routing
- โก Powered by [Vite](https://vitejs.dev)
- โจ React support with [`react-router-dom`](https://github.com/remix-run/react-router) or [`@tanstack/router`](https://github.com/tanstack/router) ๐งช or [`@tanstack/react-location`](https://github.com/tanstack/router/tree/9c8eb043e4ac350fc1d28655542e01defb0c82e5) ๐จ
- โจ Solid support with [`@solidjs/router`](https://github.com/solidjs/solid-router)
- โจ File-based MDX routes with React or Solid, requires [`@mdx-js/rollup`](https://mdxjs.com/packages/rollup) [installation and setup](/examples/react-router-mdx)
- ๐ Type-safe navigation
- ๐ Type-safe global modals
- ๐ค Route-based code-splitting and lazy-loading
- ๐ Route-based data loaders and actions
- ๐ฃ Route-based error boundary
- ๐ Nested layouts
- ๐ซ Pathless layout groups
- โ Optional static and dynamic routes
- ๐ญ Ignored routes per file or directory
## Online explorer
- โก Visit [`generouted`'s interactive playground via StackBlitz](https://stackblitz.com/github.com/oedotme/generouted/tree/main/explorer)
- ๐งฉ Explore file-based routing patterns and conventions
- ๐ Visualize the routes layouts and the resolved routes paths
- ๐ Update `src/pages/` files and see your changes reflecting
## Getting started
React Router
### React Router
In case you don't have a Vite project with React and TypeScript, check [Vite documentation to start a new project](https://vitejs.dev/guide/#scaffolding-your-first-vite-project).
#### Installation
```shell
pnpm add @generouted/react-router react-router-dom
```#### Setup
```ts
// vite.config.tsimport { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import generouted from '@generouted/react-router/plugin'export default defineConfig({ plugins: [react(), generouted()] })
```#### Usage
```tsx
// src/main.tsximport { createRoot } from 'react-dom/client'
import { Routes } from '@generouted/react-router'createRoot(document.getElementById('root')!).render()
```#### Adding pages
Add the home page by creating a new file `src/pages/index.tsx` โ `/`, then export a default component:
```tsx
export default function Home() {
returnHome
}
```Check the [routing conventions section below](#conventions).
#### Docs
You can find more details about type-safe navigation and global modals at [`@generouted/react-router` docs](/packages/react-router).
#### Examples
- [Type-safe navigation + global modals](/examples/react-router)
- [Custom integration](/examples/react-router-custom)
- [Custom integration with custom path](/examples/react-router-custom-path)
- [MDX routes](/examples/react-router-mdx)
Solid Router
### Solid Router
In case you don't have a Vite project with Solid and TypeScript, check [Vite documentation to start a new project](https://vitejs.dev/guide/#scaffolding-your-first-vite-project).
#### Installation
```shell
pnpm add @generouted/solid-router @solidjs/router
```#### Setup
```ts
// vite.config.tsimport { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
import generouted from '@generouted/solid-router/plugin'export default defineConfig({ plugins: [solid(), generouted()] })
```#### Usage
```tsx
// src/main.tsximport { render } from 'solid-js/web'
import { Routes } from '@generouted/solid-router'render(Routes, document.getElementById('root')!)
```#### Adding pages
Add the home page by creating a new file `src/pages/index.tsx` โ `/`, then export a default component:
```tsx
export default function Home() {
returnHome
}
```See more about `generouted` [routing conventions below](#conventions).
#### Docs
You can find more details about type-safe navigation and global modals at [`@generouted/solid-router` docs](/packages/solid-router).
#### Examples
- [Type-safe navigation + global modals](/examples/solid-router)
TanStack React Router โ In-progress experimental support ๐งช
### TanStack React Router โ In-progress experimental support ๐งช
[Check out the docs here](/packages/tanstack-react-router)
#### Examples
- [Basic](/examples/tanstack-react-router)
React Location โ Deprecated ๐จ
### React Location โ Deprecated ๐จ
In case you don't have a Vite project with React and TypeScript, check [Vite documentation to start a new project](https://vitejs.dev/guide/#scaffolding-your-first-vite-project).
#### Installation
```shell
pnpm add generouted @tanstack/react-location
```#### Usage
```tsx
// src/main.tsximport { createRoot } from 'react-dom/client'
import { Routes } from 'generouted/react-location'createRoot(document.getElementById('root')!).render()
```#### Adding pages
Add the home page by creating a new file `src/pages/index.tsx` โ `/`, then export a default component:
```tsx
export default function Home() {
returnHome
}
```#### Examples
- [Basic](/examples/react-location/basic)
- [Data loaders](/examples/react-location/data-loaders)
- [Modals](/examples/react-location/modals)
- [Nested layouts](/examples/react-location/nested-layouts)
## Conventions
### File and directories naming and conventions
- Routes declaration at `src/pages`
- Supports `.tsx`, `.jsx` and `.mdx` file extensions
- Optional `src/pages/_app.tsx` for an **app level layout**
- Optional `src/pages/404.tsx` for a **custom not-found page**#### Index routes
- `src/pages/index.tsx` โ `/`
- `src/pages/posts/index.tsx` โ `/posts`#### Nested routes
- `src/pages/posts/2022/index.tsx` โ `/posts/2022`
- `src/pages/posts/2022/resolutions.tsx` โ `/posts/2022/resolutions`#### Dynamic routes
- `src/pages/posts/[slug].tsx` โ `/posts/:slug`
- `src/pages/posts/[slug]/tags.tsx` โ `/posts/:slug/tags`
- `src/pages/posts/[...all].tsx` โ `/posts/*`#### Nested layouts
- By defining `_layout.tsx` in any nested directory โ `src/pages/posts/_layout.tsx`
- **Requires** using an `` component to render layout children
- All the files within the `src/pages/posts/` directory in this case, will be wrapped with that layout#### Nested URLs without nested layouts
- Route file should be outside of the nested layout directory
- Include **dots** `.` between the segments to be converted to forward slashes
- `src/pages/posts.nested.as.url.not.layout.tsx` โ `/posts/nested/as/url/not/layout`#### Pathless layouts
- Similar to nested layouts for layout definition
- By wrapping the parent directory with **parentheses** `()`
- `src/pages/(auth)/_layout.tsx`
- `src/pages/(auth)/login.tsx` โ `/login`
- Layout parent directory name is not included in the routes paths#### Global modals
- By **prefixing** the file name with a **plus sign** `+` _(thinking the modal is an extra route overlaying the current route)_
- Modals navigation available via the `useModals()` hook
- `src/pages/+info.tsx` โ `/info`
- `src/pages/+checkout/+details.tsx` โ `/checkout/details`
- `src/pages/+checkout/+payment.tsx` โ `/checkout/payment`#### Optional segments
- By **prefixing** a route segment with a **minus sign** `-` _(thinking the segment can be subtracted or removed from the route path)_
- `src/pages/-en/about.tsx` โ `/en?/about` โ `/en/about`, `/about`
- `src/pages/-[lang]/about.tsx` โ `/:lang?/about` โ `/en/about`, `/fr/about`, `/about`#### Ignored routes
- Any directory or file starts with an **underscore** `_` will be **ignored**
- `src/pages/_ignored.tsx`
- `src/pages/posts/_components/button.tsx`
- `src/pages/posts/_components/link.tsx`
### Page exports
- **Required** page component `export default Component() {...}`
- Optional page loader function `export const Loader = () => {...}`
- Optional page action function `export const Action = () => {...}`
- Optional suspense-based pending component `export const Pending = () => {...}`
- Optional error boundary component `export const Catch = () => {...}`
### Example
Directory structure
```shell
src/pages
โโโ (auth)
โ โโโ _layout.tsx
โ โโโ login.tsx
โ โโโ register.tsx
โโโ blog
โ โโโ _components
โ โ โโโ button.tsx
โ โ โโโ comments.tsx
โ โโโ [...all].tsx
โ โโโ [slug].tsx
โ โโโ _layout.tsx
โ โโโ index.tsx
โ โโโ tags.tsx
โโโ docs
โ โโโ -[lang]
โ โ โโโ index.tsx
โ โ โโโ resources.tsx
โ โโโ -en
โ โโโ contributors.tsx
โโโ +info.tsx
โโโ 404.tsx
โโโ _app.tsx
โโโ _ignored.tsx
โโโ about.tsx
โโโ blog.w.o.layout.tsx
โโโ index.tsx
```Overview
| File | Path | Convention |
| :------------------------------ | :----------------------- | :------------------------------------ |
| `(auth)/_layout.tsx` | | Pathless Layout group |
| `(auth)/login.tsx` | `/login` | Regular route |
| `(auth)/register.tsx` | `/register` | Regular route |
| `blog/_components/button.tsx` | | Ignored route by an ignored directory |
| `blog/_components/comments.tsx` | | Ignored route by an ignored directory |
| `blog/[...all].tsx` | `/blog/*` | Dynamic catch-all route |
| `blog/[slug].tsx` | `/blog/:slug` | Dynamic route |
| `blog/_layout.tsx` | | Layout for `/blog` routes |
| `blog/index.tsx` | `/blog` | Index route |
| `blog/tags.tsx` | `/blog/tags` | Regular route |
| `docs/-[lang]/index.tsx` | `/docs/:lang?/index` | Optional dynamic route segment |
| `docs/-[lang]/resources.tsx` | `/docs/:lang?/resources` | Optional dynamic route segment |
| `docs/-en/contributors.tsx` | `/docs/en?/contributors` | Optional static route segment |
| `+info.tsx` | `/info` | Modal route |
| `404.tsx` | `*` | Custom `404` _(optional)_ |
| `_app.tsx` | | Custom `app` layout _(optional)_ |
| `_ignored.tsx` | | Ignored route |
| `about.tsx` | `/about` | Regular route |
| `blog.w.o.layout.tsx` | `/blog/w/o/layout` | Route without `/blog` layout |
| `index.tsx` | `/` | Index route |
## API
### Routing
Via [`@generouted/react-router`](/packages/react-router) or [`@generouted/solid-router`](/packages/solid-router)
- `` โ file-based routing component to be render in the app entry
- `routes` โ file-based routes tree/object used by default at `` component### Routing + code-splitting and lazy-loading
Via `@generouted/react-router/lazy` or `@generouted/solid-router/lazy`
- Used instead of `@generouted/react-router` or `@generouted/solid-router` to enable lazy-loading
- Make sure to replace all imports to lazy imports โ namely at app entry and `src/pages/_app.tsx`
- Provides the same `` and `routes` exports### Plugins
Via `@generouted/react-router/plugin` or `@generouted/solid-router/plugin`
- Vite plugin for type generation and initializing type-safe components/hooks/utils
- Generates `src/router.ts` file
- Exports type-safe ``, ``, `useModals()`, `useNavigate()`, `useParams()`, `redirect()`, etc.
- Check out [`@generouted/react-router` docs](/packages/react-router) or [`@generouted/solid-router` docs](/packages/solid-router) for more details### Core
Via `@generouted/react-router/core` or `@generouted/solid-router/core`
- Available for customization, however it's recommended to use the available integrations directory via the `` component
- Check out the [custom integration example](/examples/react-router-custom)
## FAQ
How to implement protected or guarded routes?
There are multiple approaches to achieve that. If you prefer handling the logic in one place, you can define the protected routes with redirection handling within a component:
```tsx
// src/config/redirects.tsximport { Navigate, useLocation } from 'react-router-dom'
import { useAuth } from '../context/auth'
import { Path } from '../router'const PRIVATE: Path[] = ['/logout']
const PUBLIC: Path[] = ['/login']export const Redirects = ({ children }: { children: React.ReactNode }) => {
const auth = useAuth()
const location = useLocation()const authenticatedOnPublicPath = auth.active && PUBLIC.includes(location.pathname as Path)
const unAuthenticatedOnPrivatePath = !auth.active && PRIVATE.includes(location.pathname as Path)if (authenticatedOnPublicPath) return
if (unAuthenticatedOnPrivatePath) returnreturn children
}
```Then use that component (`` ) at the root-level layout `src/pages/_app.tsx` to wrap the `` component:
```tsx
// src/pages/_app.tsximport { Outlet } from 'react-router-dom'
import { Redirects } from '../config/redirects'
export default function App() {
return (
...
)
}
```You can find a full example of this approach at [Render template](https://github.com/oedotme/render/blob/main/src/config/redirects.tsx)
How to use with Hash or Memory Routers?
You can use the exported `routes` object to customize the router or to use hash/memory routers:
```tsx
import { createRoot } from 'react-dom/client'
import { RouterProvider, createHashRouter } from 'react-router-dom'
import { routes } from '@generouted/react-router'const router = createHashRouter(routes)
const Routes = () =>createRoot(document.getElementById('root')!).render()
```
## License
MIT