One provides full TypeScript support for route parameters and loader props, automatically generating types from your file system routes.
When you create route files with dynamic segments like [slug] or [...rest], One automatically generates TypeScript types that make your route parameters fully type-safe.
All route types are generated in app/routes.d.ts:
declare module 'one' {
export namespace OneRouter {
export interface __routes<T extends string = string> {
StaticRoutes: '/' | '/about' | '/docs'
DynamicRoutes: `/docs/${OneRouter.SingleRoutePart<T>}`
DynamicRouteTemplate: '/docs/[slug]'
IsTyped: true
RouteTypes: {
'/docs/[slug]': {
Params: { slug: string }
LoaderProps: { path: string; params: { slug: string }; request?: Request }
}
}
}
}
}
createRoute HelperThe recommended approach is to use the createRoute helper, which provides fully typed methods:
app/docs/[slug].tsx
import { createRoute } from 'one'
const route = createRoute<'/docs/[slug]'>()
export const loader = route.createLoader(async ({ params }) => {
// params.slug is string - fully typed!
return { doc: await fetchDoc(params.slug) }
})
export default function DocPage() {
const params = route.useParams()
// params.slug is string - fully typed!
return <div>Viewing: {params.slug}</div>
}
You can also use the RouteType helper to get types for any route:
app/docs/[slug].tsx
import type { RouteType } from 'one'
type Route = RouteType<'/docs/[slug]'>
export const loader = ({ params }: Route['LoaderProps']) => {
// params.slug is fully typed!
return { doc: await fetchDoc(params.slug) }
}
Enable the experimental typedRoutesGeneration option to automatically insert type helpers into your route files:
vite.config.ts
export default {
plugins: [
one({
router: {
experimental: {
typedRoutesGeneration: 'runtime', // or 'type'
},
},
}),
],
}
false (default): No auto-generation, manually add types yourself'type': Auto-inserts type-only helpers:import type { RouteType } from 'one'
type Route = RouteType<'/your/[route]'>
'runtime': Auto-inserts runtime helpers:import { createRoute } from 'one'
const route = createRoute<'/your/[route]'>()
The insertion happens automatically when route files are created or modified, with proper spacing and without modifying your existing loader code.
Generate route types manually using the One CLI:
npx one generate-routes # Generate types only npx one generate-routes --typed=runtime # Generate + inject runtime helpers npx one generate-routes --typed=type # Generate + inject type helpers
Note: The CLI command requires --typed to inject helpers. The typedRoutesGeneration config option works automatically during development (watches files and injects on changes), while the CLI flag gives you manual control when needed.
app/blog/[category]/[year]/[slug].tsx
import { createRoute } from 'one'
const route = createRoute<'/blog/[category]/[year]/[slug]'>()
export const loader = route.createLoader(({ params }) => {
// All params are fully typed!
const { category, year, slug } = params
// category: string, year: string, slug: string
return { post: await fetchPost(category, year, slug) }
})
app/files/[...path].tsx
import { createRoute } from 'one'
const route = createRoute<'/files/[...path]'>()
export const loader = route.createLoader(({ params }) => {
// params.path is string[] - fully typed!
const filePath = params.path.join('/')
return { file: await fetchFile(filePath) }
})
{ slug: string } instead of complex type aliasesapp directory for route files[slug] and [...rest]app/routes.d.tsThe RouteType helper looks up the generated types from routes.d.ts and provides dot-notation access to Params and LoaderProps.
Edit this page on GitHub.