Components and Hooks
<Link>
This component can be used as a drop-in replacement for <a>
tags. Internally, it renders an <a>
tag so that statically rendered links will work even if JavaScript is disabled.
Props
active
: boolean
(optional)
Allows you to explicit enable or disable the activeClassName
and activeStyle
props.
activeClassName
: string
(optional)
Will be added to your link’s className
if the app’s current URL matches the href
prop, or if the active
prop is set to true
.
activeStyle
: object
(optional)
Will be merged into your link’s style
if the app’s current URL matches the href
prop, or if the active
prop is set to true
.
exact
: bool
(optional)
If true, the link will only be considered to “match” the current URL if it is an exact match.
By default, a partial match at the beginning of the current URL will also be considered a match. This facilitates nav links, which often need to be highlighted when child pages are active.
href
: string
(required)
The url to navigate to; identical to the href
attribute of a HTML <a>
tag.
prefetch
: boolean
(optional)
If specified, the linked page’s content will be fetched as soon as the link is rendered.
<NotFoundBoundary>
You can wrap a <NotFoundBoundary>
around a view to show a “not found” message when no route can be found to be rendered by the view.
Props
render: (error: NotFoundError) => React.Node
Example
function Layout() {
return (
<NotFoundBoundary render={() =>
<h1>Not Found</h1>
}>
<View />
</NotFoundBoundary>
)
}
<Router>
Your app’s <Router>
component is responsible for subscribing to the latest navigation state, and passing that state to Navi’s other components.
By default, <Router>
creates a navigation
object internally, passing through the routes
, basename
, context
and history
props. However, if you’d like to define your navigation
object manually — i.e. for static or server rendering support — then you can pass it in via the navigation
prop.
Props
navigation
: Navigation
Your app’s Navigation
object, as returned from createBrowserNavigation()
or createMemoryNavigation()
By default, this will be created internally.
basename
: string
(optional)
The basename
to use for the created navigation
object. Only used when a navigation
prop is not manually supplied.
children
: React.ReactNode
(optional)
If you don’t pass in any children, then a <View>
will be rendered by default.
context
: Context
(optional)
The <Router>
object will keep your navigation
object’s context in sync with this prop by calling it’s setContext()
method.
This will be used even if you supply a navigation
prop.
hashScrollBehavior
: 'smooth' | 'auto'
(optional)
Specify whether to use native smooth scrolling using window.scroll(). By default, does not use smooth scrolling, as it is not supported by a number of browsers.
history
: History
(optional)
The history
to use for the created navigation
object. Only used when a navigation
prop is not manually supplied.
routes
: Matcher
The routes
to use for the created navigation
object. Only used when a navigation
prop is not manually supplied.
<View>
Renders the content for the next matched view that hasn’t already been rendered.
If the content is a React element or component, then the children
render function is optional, and that content will be rendered by default. Otherwise, you’ll need to provide a child function to specify how to render the route content.
Suspense
When the page initially loads, it can be the case that no content is available — even for previous routes. In this case, <View>
will use React Suspense to pause execution until content is available. However, React requires you to wrap the suspended <View>
with a <Suspense fallback>
, so that you can decide what will be shown in place of the not-yet-ready content. If you’ve used React.lazy()
, then this should be familiar — otherwise, check out the example below.
As React doesn’t yet support Suspense for server-side rendering, you’ll want to avoid using <Suspense>
for statically rendered apps. Instead, wait for Navi’s initial content to render before calling ReactDOMServer.render()
by creating a custom navigation
object and using its navigation.getRoute()
function, before passing the navigation
object to your <Router>
component’s navigation
prop.
Props
children: (view, route) => React.ReactNode
(optional)
If provided, the component’s children
must be a render prop that receives the view’s value as its first argument, and the current Route as its second.
If a children
prop is not provided, the default behavior is to render the next unrendered view from route.views
.
Examples
Basic Usage
A <View>
can be placed anywhere inside a <Router>
component — it doesn’t have to be a direct descendent. You’ll often want your <View>
to be nested within a <Suspense>
component, or a <Layout>
component, like so:
import { Router, View } from 'react-navi'
ReactDOM.render(
<Router navigation={navigation}>
<Layout>
<View />
</Layout>
</Router>,
document.getElementById('root')
);
Usage with React Suspense
By wrapping your content in a React <Suspense>
element, you can specify a fallback element to display while your app’s initial content is loading.
Note that until <Suspense>
gets server-rendering support, this only makes sense for apps that are not pre-rendered with ReactDOMServer.render()
. For static or server rendered apps, use navigation.getRoute()
to wait for the initial content to load before hydrating the pre-rendered HTML with ReactDOM.hydrate()
.
import React, { Suspense } from 'react'
import { Router, View } from 'react-navi'
import Layout from './Layout'
ReactDOM.render(
<Router routes={routes}>
<Layout>
<Suspense fallback={null}>
<View />
</Suspense>
</Layout>
</Router>,
document.getElementById('root')
);
useActive()
useActive(
href: string | Partial<LocationObject>,
options?: { exact?: boolean }
)
Returns true
when the current route matches the specified href. You can use this along with useLinkProps()
to create your own custom styled links.
Examples
// Will be true if currently at '/contacts'
let isViewingContactsIndex = useActive('/contacts')
// Will be true if viewing '/contacts' or any page nested within it
let isViewingContacts = useActive('/contacts', { exact: false })
useCurrentRoute()
A React Hook that allows you to access the latest non-busy Route
, which contains all information that Navi knows about the current page, including:
data
title
url
views
- etc.
You can also access the result of useCurrentRoute()
by passing a child function to the <CurrentRoute>
component, similar to the API for React context consumers.
Example
You can use useCurrentRoute()
to retrieve the current page’s title
, and display it as part of a <Layout>
component.
import { useCurrentRoute } from 'react-navi'
function Layout({ children }) {
let route = useCurrentRoute()
return (
<div className="Layout">
<h1>{route.title}</h1>
{children}
</div>
)
}
useLinkProps()
useLinkProps(options: {
href: string | Partial<LocationObject>,
onClick?: Function,
prefetch?: boolean,
}): HTMLAnchorProps
This hook accepts a subset of the props that <Link>
accepts, and returns an object with href
, onClick
, and any other props that you’d want to spread onto an <a>
. You can then spread these props onto an <a>
to make custom links components.
If the href
is specified as a relative URL, it will be treated as relative to the current url. Navi normalizes the current URL so that trailing slashes have no effect.
Any unknown options will be passed through to the returned props object.
Use this along with useActive()
to create components that change their styles depending on the current URL.
Examples
Navigation links with styled-components
Say that you’ve built a <ListItem>
component with styled-components. You’d like to make the whole item behave as a link — and you also want to highlight the item when the link points to the current page.
useLoadingRoute()
A React Hook that outputs the Route
object for any page whose content is currently being fetched. If no page is currently being fetched, then it outputs undefined
.
Use useLoadingRoute()
to display a loading overlay when the user has requested a navigation that hasn’t completed yet.
You can also access the result of useLoadingRoute()
by passing a child function to the <LoadingRoute>
component, similar to the API for React context consumers.
Example
import { useLoadingRoute } from 'react-navi'
function Layout({ children }) {
let loadingRoute = useLoadingRoute()
return (
<div className="Layout">
<BusyIndicator show={!!loadingRoute} />
{ children }
</div>
)
}
useNavigation()
A React Hook that returns your app’s navigation
store, which can be used to facilitate programmatic navigation.
You can also access the result of useNavigation()
by passing a child function to the <NaviConsumer>
component, similar to the API for React context consumers.
Example
You can use useNavigation()
to programmatically navigate after a form is successfully submitted.
import React, { useState } from 'react'
import { useNavigation } from 'react-navi'
function EmailForm() {
let navigation = useNavigation()
let [email, setEmail] = useState()
function handleSubmit() {
navigation.navigate('/thanks')
}
return (
<form onSubmit={handleSubmit} className="EmailForm">
<input value={email} onChange={e => setEmail(e.target.value)} />
<button>Join 13,000 other subscribers</button>
</div>
)
}
useView()
Silimar to useViewElement()
, this hook can act in place of the <View />
element. However, instead of returning a ready-to-go-element, it returns an object with the view’s raw content
, a connect()
function that you’ll need to wrap it with, and an assortment of other bits and pieces.
{
chunks: Chunk[]
connect: (node: React.ReactNode) => React.ReactElement<any>
content: any
element: React.ReactElement<any>
final: boolean
head: any
}
The main reason you’d want to use the useView()
hooks is when your view content contains data that you need to manually convert to a React element. For example, here’s how it’s used in create-react-blog to extract a readingTime
variable from the view content:
import { useCurrentRoute, useView } from 'react-navi'
function BlogPostLayout({ blogRoot }) {
let { title, data, url } = useCurrentRoute()
let { connect, content, head } = useView()
let { MDXComponent, readingTime } = content
// The content for posts is an MDX component, so we'll need
// to use <MDXProvider> to ensure that links are rendered
// with <Link>, and thus use pushState.
return connect(
<>
{head}
<article className={styles.container}>
<ArticleMeta
blogRoot={blogRoot}
meta={data}
title={title}
url={url}
readingTime={readingTime}
/>
<MDXComponent />
</article>
</>,
)
}
useViewElement()
Returns an element that renders exactly the same content that a <View />
element would render at the time that useViewElement()
was called.
function App() {
let view = useViewElement()
return (
<Layout>
{view}
</Layout>
)
}
Generally, this behaves exactly the same as rendering a <View />
. The main use for useViewElement()
is in implementing animated transitions between routes. In this case, you’ll often need to render both the current view and the previous view at the same time. And while <View />
will always render the current view, it’s possilbe to store the view
element returned by useViewElement()
until it is no longer needed.
For example, here’s an example of animated transitions using react-spring and useViewElement()
: