Pattern Syntax
Route patterns use a simple syntax for matching URLs and extracting parameters:
Static segments : /blog/posts
Dynamic segments : /blog/:slug - Matches any value
Optional segments : /blog/:slug? - Matches with or without the segment
Wildcard segments : /files/* - Matches remaining path
import { route } from 'remix/fetch-router/routes'
let routes = route ({
// Static route
home: '/' ,
// Dynamic parameter
post: '/blog/:slug' ,
// Multiple parameters
comment: '/blog/:postSlug/comments/:commentId' ,
// Optional parameter
search: '/search/:query?' ,
// Wildcard (matches everything after /files/)
files: '/files/*' ,
})
Type-Safe Parameters
Route parameters are automatically inferred from your patterns and fully type-checked:
router . map ( routes , {
actions: {
post ({ params }) {
// params.slug is a string
console . log ( params . slug )
return new Response ( `Post: ${ params . slug } ` )
},
comment ({ params }) {
// Both parameters are typed
console . log ( params . postSlug , params . commentId )
return new Response ( 'Comment' )
},
search ({ params }) {
// Optional parameter is string | undefined
if ( params . query ) {
console . log ( `Searching for: ${ params . query } ` )
}
return new Response ( 'Search' )
},
files ({ params }) {
// Wildcard matches are available as params['*']
console . log ( params [ '*' ]) // e.g., "documents/report.pdf"
return new Response ( 'Files' )
},
},
})
Declaring Routes
There are several ways to declare routes:
Simple String Patterns
The simplest way is to use string patterns directly:
let routes = route ({
home: '/' ,
about: '/about' ,
contact: '/contact' ,
})
type HomeRoute = typeof routes . home
// Route<'ANY', '/'>
These routes match any HTTP method (GET, POST, etc.).
Method-Specific Routes
Specify the HTTP method in the route definition:
let routes = route ({
getPosts: { method: 'GET' , pattern: '/posts' },
createPost: { method: 'POST' , pattern: '/posts' },
updatePost: { method: 'PUT' , pattern: '/posts/:id' },
deletePost: { method: 'DELETE' , pattern: '/posts/:id' },
})
type GetPostsRoute = typeof routes . getPosts
// Route<'GET', '/posts'>
The HTTP method to match: 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'
pattern
string | RoutePattern
required
The URL pattern to match
Method Helper Functions
Use helper functions for common HTTP methods:
import { get , post , put , del } from 'remix/fetch-router/routes'
let routes = route ({
getPosts: get ( '/posts' ),
createPost: post ( '/posts' ),
updatePost: put ( '/posts/:id' ),
deletePost: del ( '/posts/:id' ),
})
Available helpers:
get(pattern) - GET request
post(pattern) - POST request
put(pattern) - PUT request
patch(pattern) - PATCH request
del(pattern) - DELETE request (aliased from createDeleteRoute)
head(pattern) - HEAD request
options(pattern) - OPTIONS request
Generating URLs
Every route has an href() method for generating type-safe URLs:
let routes = route ({
home: '/' ,
post: '/blog/:slug' ,
comment: '/blog/:postSlug/comments/:commentId' ,
})
// Simple route (no parameters)
routes . home . href () // "/"
// Route with one parameter
routes . post . href ({ slug: 'hello-world' }) // "/blog/hello-world"
// Route with multiple parameters
routes . comment . href ({
postSlug: 'hello-world' ,
commentId: '123' ,
}) // "/blog/hello-world/comments/123"
// Add query parameters
routes . post . href (
{ slug: 'hello-world' },
{ searchParams: { sort: 'date' , order: 'desc' } },
) // "/blog/hello-world?sort=date&order=desc"
Parameters to fill into the route pattern. Required if the pattern has dynamic segments.
Additional options for URL generation searchParams
Record<string, string> | URLSearchParams
Query string parameters to append to the URL
Hash fragment to append to the URL (without the # prefix)
Using href() in HTML
Use href() to generate type-safe links and form actions:
import { html } from 'remix/html-template'
import { createHtmlResponse } from 'remix/response/html'
router . get ( routes . home , () => {
return createHtmlResponse ( html `
<html>
<body>
<nav>
<a href=" ${ routes . home . href () } ">Home</a>
<a href=" ${ routes . post . href ({ slug: 'hello-world' }) } ">Hello World</a>
</nav>
<form method="POST" action=" ${ routes . createPost . href () } ">
<input type="text" name="title" />
<button type="submit">Create</button>
</form>
</body>
</html>
` )
})
Pattern Matching
Routes can test whether a URL matches their pattern:
let route = new Route ( 'GET' , '/blog/:slug' )
// Match a URL
let match = route . match ( 'https://example.com/blog/hello-world' )
if ( match ) {
console . log ( match . params . slug ) // "hello-world"
}
// No match
let noMatch = route . match ( 'https://example.com/about' )
console . log ( noMatch ) // null
Nested Routes
Organize routes hierarchically with nested objects:
let routes = route ({
home: '/' ,
blog: {
index: '/blog' ,
show: '/blog/:slug' ,
comments: {
index: '/blog/:slug/comments' ,
show: '/blog/:slug/comments/:commentId' ,
},
},
admin: {
dashboard: '/admin' ,
users: {
index: '/admin/users' ,
show: '/admin/users/:id' ,
edit: '/admin/users/:id/edit' ,
},
},
})
// Access nested routes
routes . blog . show . href ({ slug: 'hello' })
routes . blog . comments . show . href ({
slug: 'hello' ,
commentId: '123' ,
})
routes . admin . users . edit . href ({ id: '456' })
Base Patterns
Define a base pattern for all routes in a group:
import { createRoutes } from 'remix/fetch-router/routes'
// All routes will be prefixed with /api/v1
let apiRoutes = createRoutes ( '/api/v1' , {
users: '/users' ,
posts: '/posts' ,
comments: '/comments' ,
})
apiRoutes . users . href () // "/api/v1/users"
apiRoutes . posts . href () // "/api/v1/posts"
base
string | RoutePattern
required
The base pattern to prepend to all routes
RoutePattern Class
For advanced use cases, you can work with RoutePattern directly:
import { RoutePattern } from '@remix-run/route-pattern'
let pattern = new RoutePattern ( '/blog/:slug' )
// Generate URLs
pattern . href ({ slug: 'hello' }) // "/blog/hello"
// Match URLs
let match = pattern . match ( 'https://example.com/blog/hello' )
console . log ( match ?. params . slug ) // "hello"
// Join patterns
let base = new RoutePattern ( '/api' )
let joined = base . join ( '/users/:id' )
joined . href ({ id: '123' }) // "/api/users/123"
Parameter Constraints
While route patterns don’t support regex constraints, you can validate parameters in your action:
router . get ( '/users/:id' , ({ params }) => {
// Validate that id is a number
if ( ! / ^ \d + $ / . test ( params . id )) {
return new Response ( 'Invalid user ID' , { status: 400 })
}
return Response . json ({ userId: params . id })
})
Wildcard Matching
Wildcard segments match the rest of the path:
let routes = route ({
files: '/files/*' ,
})
router . get ( routes . files , ({ params }) => {
// Access wildcard match with params['*']
let filePath = params [ '*' ] // e.g., "documents/report.pdf"
return new Response ( `File: ${ filePath } ` )
})
The wildcard segment must be the last segment in the pattern.
Next Steps
Middleware Add reusable functionality to routes
Controllers Organize route actions with controllers
Forms Learn about form routes and RESTful resources