The provided Router is an extended version of the Wouter Router, that have been modified to :
- Perform delayed route transition (pre-loading the route and fetching data before triggering the navigation)
- Prevent duplicated history entries
Array of key-value pair objects defining the routes of the application.
Expected structure for a "page" object :
| Key | Description |
|---|---|
pathstring |
The path for which we want to render the component. • /segment/ normal segment• /:segment/ dynamic segment• /segment?/ optional segment• /app-* wildcard to match anythingYou can find more details about the expected path structure in the Wouter Router doc. |
propsFetcher(optional) function |
Async function in charge of fetching a JSON object that will be given as props to the page component. It should accept 2 arguments : • currentUrl : a RelativeUrl instance representing current url (see url-toolbox package doc)• abortController : an AbortController instance that have to be provided to the fetch call✱ it's recomanded to use fetchJsonData |
ComponentReact Component OR importComponentasync import function |
For the page component, we have two options. • Component key :To directly provide a react component (it will be included in the bundle for any page). • importComponent key :To provide an async import function in charge of importing the component when it's needed ex: () => import('./pages/hello/page.tsx')✱ If multiple paths use the same asynchronously imported component, they should all be using the same function instance (the instance is used to keep track of loaded components). |
Here's a basic example defining two routes, a lazy import function and a page props fetcher :
import { fetchJsonData } from "@core:utils/fetch-json-data/v1/utils"
import HomePage from './pages/home/page.tsx'
/** lazy imported components
*
* ⚠️ there should be only one function instance
* in charge of loading each lazy component.
* If two paths display the same component
* they should both use the same function instance.
*/
const lazyLoaders = {
HelloPage: () => import('./pages/hello/page.tsx')
}
/** props fetcher */
async function fetchPageProps(url, abortController) {
// deriving API endpoint from current url
url.searchParams.set('_data_', 'json')
return fetchJsonData( url, {
fetchJsonOpts: {
abortController: abortController,
cache: {
strategy: 'CACHE_FIRST',
maxAgeS: 60, // 1 min
}
}
}
)
}
/** routes declaration */
const pages = [
{
path: '/',
Component: HomePage,
// props fetching function
propsFetcher: fetchPageProps,
}, {
// path with dynamic "name" segment
path: 'hello/:name/',
// asyc component import function
importComponent: lazyLoaders.HelloPage,
// no props fetcher
// -> so component doesn't expect any props
}
]Defines routes loading behaviours.
ℹ️ Loaders are defined in two places around the page components :
-
At the routes level :
it is displayed by the router if the route pre-loading on navigation exceeds a given duration. -
As fallback in suspense boundaries:
It's a secondary loader, displayed if the page isn't ready at render time. For example on initial load of a CSR fetching page (not preloaded).
| Key | Description |
|---|---|
Loader(optional) React Component |
Loader component that will be used (both for the suspense boundaries and on navigation) |
suspenseFallbackOpts(optional) options object |
Options regarding the suspense fallback loader used for pages that are lazy loaded or fetches props Keys : • deactivate optional boolean.If true, suspense boundaries surrounding pages will not receive any fallback components. |
pagePreFetchOpts(optional) options object |
Options regarding the loader displayed during page pre-loading on client side navigation. Keys : • displayLoader optional boolean.If false, the loader is not displayed when the loading state is activated by the timer. • hidePageOnLoad optional boolean.If false, the page component is not hidden when the loading state is activated by the timer. • timeoutMs optional number (default 500ms).Minimum page transition duration after which we trigger the loading state on client side navigation. |
Loader options example :
const loaderOptions = {
// Loader component
Loader: () => <div><p>Loading ...</p></div>,
// We do not add the loader as fallback component
// in the page suspense boundaries
suspenseFallbackOpts: {
deactivate: true
}
// If navigation is longer than 800ms
// we display the loader without hidding the previous page component
pagePreFetchOpts: {
displayLoader: true,
hidePageOnLoad: false,
timeoutMs: 800
}
}see also : loading state handling documentation
Error Boundary component props (Fallback and optional errorHandlingFunc).
Read error boundary documentation for more informations.
-
ssrPath: relative URL string of the page we want to render. -
ssrProps: key-value pair object that is provided as props to the page rendered for that path.
ℹ️ They are automatically provided to the root App component by the server when performing SSR, and the default provided App component forwards it to the Router component.
Component rendering the matching page component for a given route.
It doesn't take any props, but must be rendered under a Router component (it consumes a context provided by Router to get available routes).
Included in the root App component by default.
Example :
import Router, {type RouterProps_T} from '@core:routing/v1/router'
import Routes from '@core:routing/v1/routes'
...
function App(props) {
return <Router
pages={pages}
loaderOptions={loaderOptions}
errorBoundaryOptions={errorBoundaryOptions}
{... props} // optional "ssrPath" & "ssrProps"
>
<Layout>
<Routes/>
</Layout>
</Router>
}Wrapper around Wouter's Link component for internal links.
- it forces any given URL to a relative URL
- adds different classes depending on the link state (active, broken ...)
ℹ️ Uses Extended URLs from url-toolbox package, read doc for more informations.
It accept as props any HTML anchor tag attribute.
And href is extended to accept values of type string, URL, ExtendedURL or undefined (will result in a default '/' url).
🛠️ internally, provided href is converted to a RelativeUrl instance.
ℹ️ if the parsing of the given href to a URL fails, the component returns the content of the link in a <span> instead of a <a> and a specific class is applied.
Example :
import { RelativeUrl } from "url-toolbox"
import { RelativeLink } from "@core:routing/v1/link"
function NavBar(props) {
...
const articleUrl = new RelativeUrl(
'/articles/1-my-title/'
)
return <>
<RelativeLink
href='/'
className='home-page-link'
>Home Page</RelativeLink>
<RelativeLink
href={articleUrl}
>New Article</RelativeLink>
...
</>
}Classes are added to the html anchor tag (or span if url is invalid) to reflect the status of the link:
| Class name | Description |
|---|---|
"link-active" |
Class applied if current page's URL pathname matches link's URL pathname ℹ️ Ignores search & hash |
"link-partial-match" |
Class applied if current page's URL pathname start with link's URL pathname ℹ️ Ignores search & hash |
"link-broken" |
Class applied if the provided url is invalid ℹ️ For invalid URLs the tag produces is a <span> |