unplugin-remix-router
generates a react-router
file that depends on remix v2 file router convention.
For more information, please refer to the React Router documentation. Note that it follows the Remix file convention.
pnpm i -D unplugin-remix-router
Vite
// vite.config.ts
import remixRouter from 'unplugin-remix-router/vite'
export default defineConfig({
plugins: [
remixRouter({ /* options */ }),
],
})
Example: playground/
Rollup
// rollup.config.js
import remixRouter from 'unplugin-remix-router/rollup'
export default {
plugins: [
remixRouter({ /* options */ }),
],
}
Webpack
// webpack.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-remix-router/webpack')({ /* options */ })
]
}
esbuild
// esbuild.config.js
import { build } from 'esbuild'
import remixRouter from 'unplugin-remix-router/esbuild'
build({
plugins: [remixRouter()],
})
// main.tsx
import { routes } from 'virtual:routes'
export const router = createBrowserRouter(routes)
createRoot(document.getElementById('app')!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
)
for deep understanding how filebased routing work, see examples in remix v2 file router convention
- app/
- routes/
- _index.tsx
- about.tsx
- countries.tsx # layout
- countries.yemen/route.tsx
- countries.wusab/route.tsx
- main.tsx # `index.html` and `main.jsx` are the project starter point
every route can export one of following, see React Router for more.
use example in playground/
as starter kit, or reactive template. also there is a version of official react-router-tutorial with unplugin-remix-router
export const caseSensitive = false
export const id = 'main-page'
// every `loader` should exported by name `clientLoader` from v2
export async function clientLoader() {}
// every `action` should exported by name `clientAction` from v2
export async function clientAction() {}
// every component should exported as `default` no matter what is the name from v2
export default function Component() {
return <h1>Hello Remix Router!</h1>
}
export function ErrorBoundry() {
return <h1>Something went wrong</h1>
}
export function shouldRevalidate({ currentUrl }) {
return currentUrl.pathname === '/meal-plans/new'
}
export const handler = {
attachedData: {
key: 'value'
}
}
add following to vite-env.d.ts
declare module 'virtual:routes' {
export const routes: any // Adjust the type accordingly based on your routes structure
}
By default, Vite and other JavaScript bundlers package all project files into a single file. While this is often beneficial, it can result in slower initial load times for the project. To address this, you can implement lazy loading for routes, allowing the bundler to split the code for each route into separate files. This approach can improve the performance of the initial load.
To implement this, simply add .lazy to route names (note: this applies only to routes, not components). Consequently, the project structure will look like this:
- app/
- routes/
- _index.tsx
- about.lazy.tsx # lazy route, will not included in main project file
- countries.tsx
- countries.yemen/route.tsx
- countries.wusab/route.lazy.tsx # also lazy route, will not included in main project file
- main.tsx
Most React Router commands are accessed through hooks, such as const navigate = useNavigate()
. However, there are times when you need to access these functions within state manager actions. By defining a global router in main.jsx, you can access many of these functions from anywhere in your application. Here’s how you can do it:
// main.jsx
import { createBrowserRouter } from 'react-router-dom'
import { createRoot } from 'react-dom/client'
export const router = createBrowser(/* ... */)
createRoot(/* ... */)
// Now you can import `router` from any file and use its methods
// For example, to navigate programmatically:
router.navigate('/login')