React-Static is packed with awesome components, hooks, and functions to help you be productive. Exports located in the react-static
package are meant to be used in the browser context, in your application code and in browser.api.js
. Exports located in the react-static/node
package are meant to be used in the node context like static.config.js
and node.api.js)
react-static
react-static/node
The following functions are available via the react-static
import. They are primarily for use in the browser environment, but are also available for use in browser.api.js
plugin files, too.
React Static handles all of your routing for you using react-router
under the hood. All you need to do is import Routes
and specify where you want to render them:
// App.js
import { Root, Routes } from 'react-static'
export default () => (
<Root>
<Routes />
</Root>
)
The routes that will be rendered are the routes returned by the getRoutes
function of this config.
Occasionally, you may need to render the automatic <Routes>
component in a custom way. The most common use-case is for rendering animated routes, described further in the animated-routes guide. To do this, utilize a render
prop:
import React from 'react'
import { Root, Routes } from 'react-static'
export default () => (
<Root>
<Routes
render={({ routePath, getComponentForPath }) => {
// The routePath is used to retrieve the component for that path
const element = getComponentForPath(routePath)
return element
}}
/>
</Root>
)
Render Props - These special props are sent to your render
function:
getComponentForPath(pathname) => ReactElement
- Takes a pathname and returns an element (if a route exists) to render that path. Returnsfalse
if no route is found.routePath: String
- The path of the current route. You can pass it togetComponentForPath
.
Via suspense, the useRouteData
hook asynchronously provides the results of a routes's getData
function as defined in your static.config.js
. If you are unable to use a hook in your component, you may also use the RouteData
component or withRouteData
HOC to access routeData, though we suggest refactoring to hooks for future releases.
static.config.js
module.exports = {
getRoutes: () => [
{
path: '/top-100-songs',
getData: async () => ({
songs: await SpotifyAPI.getTopSongs(100),
}),
},
],
}
TopSongs.js
import { useRouteData } from 'react-static'
export default () => {
const { songs } = useRouteData()
return (
<div>
<h1>Top 100 Spotify Songs</h1>
<ul>
{songs.map(song => (
<li key={song.id}>{song.title}</li>
))}
</ul>
</div>
)
}
Via suspense, the useSiteData
hook asynchronously provides the results of the getSiteData
function as defined in your static.config.js
.
If you are unable to use a hook in your component, you may also use the SiteData
component or withSiteData
HOC to access siteData, though we suggest refactoring to hooks for future releases.
static.config.js
module.exports = {
getSiteData: () => ({
siteTitle: 'React Static',
metaDescription: 'A progressive static-site framework for React',
}),
}
Home.js
import { useSiteData } from 'react-static'
export default () => {
const { siteTitle, metaDescription } = useSiteData()
return (
<div>
Welcome to {siteTitle}! {metaDescription}
</div>
)
}
Note: Make sure to wrap components using useSiteData()
with React's <Suspense fallback="..."></Suspense>
. More information on the subject is available here.
Head
is a react component for managing tags in the document's head
. Use it to update meta tags, title tags, etc.
- It can be used anywhere in your app.
- It can be used in multiple places at the same time.
- For more information, see the React-Helmet library that React Static uses to accomplish this.
import { Head } from 'react-static'
export () => (
<div>
<Head>
<meta charSet="UTF-8" />
<title>This is my page title!</title>
</Head>
<div>
My page content...
</div>
</div>
)
The usePrefetch
hook binds the prefetching of a specific path
's assets to the visibility of an element. When the ref's element becomes visible in the viewport, the template and data required to render the route for the path
will be prefetched.
This increases the chance that if the user then navigates to that route, they will not have to wait for the required data to load.
import { useRef } from 'react'
import { usePrefetch } from 'react-static'
export default () => {
// Use it to create a ref
const myRef = usePrefetch('/blog')
// or pass your own ref
const myRef = useRef()
usePrefetch('./blog', myRef)
return (
<Link to="/blog" ref={myRef}>
Go to blog
</Link>
)
}
Note: It's critical that the ref.current
value passed to usePrefetch
resolves to an actual dom element. Otherwise an error will be thrown.
prefetch
is an imperative version of the usePrefetch
hook that you can use anywhere in your code.
Example:
import { prefetch } from 'react-static'
const myFunc = async () => {
const data = await prefetch('/blog')
console.log('The preloaded data:', data)
}
addPrefetchExcludes
allows you to register dynamic route exclusions at runtime, so as to not produce 404 errors when attempting to preload static data / templates that link to these routes.
- Arguments
excludes: Array[string | RegExp]
- An array of strings and/or RegExp objectsstring
- Any routes starting with this string will be excludedRegExp
- Any routes matching this regular expression will be excluded
- Returns nothing
Example:
import { addPrefetchExcludes } from 'react-static'
// Run this before your app code
addPrefetchExcludes(['dynamic', /admin/i])
// Your app code
// ...
The following functions are available as exports from the react-static/node
module. They are a separate import so that they may be used primarily in your static.config.js and node.api.js plugin files.
Intended for use in your static.config.js
during development. When called it will rebuild all of your routes and routeData by calling config.getRoutes()
again. Any new routes or data returned will be hot-reloaded into your running development application. Its main use cases are very applicable if your routes or routeData are changing constantly during development and you do not want to restart the dev server. You can use this method to reload when local files are changed, update at a set timing interval, or even subscribe to an event stream from an API or CMS.
- Arguments
paths: Array
- The paths to reload (defaults to all).
- Returns a
Promise
Example:
// static.config.js
import { rebuildRoutes } from 'react-static/node'
// Reload Manually
rebuildRoutes()
// Reload when files change
import chokidar from 'chokidar'
// Ensure routes are built at least once before rebuilding routes
let areRoutesBuilt = false
chokidar.watch('./docs').on('all', () => isReady && rebuildRoutes())
// Reload from API or CMS event
YourFavoriteCMS.subscribe(rebuildRoutes)
// Reload your routes every 10 seconds
setInterval(rebuildRoutes, 10 * 1000)
// ETC!
export default {
getRoutes: () => {
areRoutesBuilt = true
// This will run each time `rebuildRoutes` is called
},
}
A utility function to aid in splitting an array of items into separate pages for use in your static.config.js
- Arguments
options{}
- Requireditems: Array
- Required - The array of items to split into pagespageSize: Int
- Required - The number of items on each pageroute{}: Object
- Requiredpath: String
- Required - The base path that all pages will sharetemplate: String
- The base component that all pages will share
decorate: Function
- Required- Arguments:
items: Array
- The items for the given pagepageIndex: Int
- The page index for the given pagetotalPages: Int
- The total number of pages that were generated
- Returns an
Object
that will decorate the base route. In most cases, this will probably include thegetData
andchildren
keys, but can contain any route supported keys
- Arguments:
pageToken: String
- The string that will be used to prefix each page.
- Returns an array of routes objects
Example:
// static.config.js
import { makePageRoutes } from 'react-static/node'
export default {
getRoutes: () => {
const posts = [...]
return [
...makePageRoutes({
items: posts, // Use the posts array as items
pageSize: 5, // Use 5 items per page
pageToken: 'page', // use page for the prefix, eg. blog/page/3
route: {
// Use this route as the base route
path: 'blog',
template: 'src/containers/Blog',
},
decorate: (items, pageIndex, totalPages) => ({
// For each page, supply the posts, page and totalPages
getData: () => ({
posts: items,
currentPage: pageIndex,
totalPages,
}),
// Make the routes for each blog post
children: items.map(post => ({
path: `/blog/post/${post.id}`,
template: 'src/containers/Post',
getData: () => ({
post,
}),
})),
}),
}),
]
}
}
Each route's getData
function results in a separate data file for each route being stored as JSON next to the routes HTML on export. This covers the 90% use case for data splitting, but if you want even more control and want to optimize repeated data across routes, you can use this function to create shared data fragments for use in your routes.
These shared data fragments can be placed in any route's sharedData
object. At runtime, the shared data will only be requested once per session and automatically merged into the route data, which you can consume normally through the RouteData
or withRouteData
component/HOC pair.
Example
Consider a large and heavy menu structure that is present only on the blog portion of your site. In this case, the menu data should only be loaded on the pages that use it, and only once per session (cached), instead of on every page individually. First we would use the createSharedData
function and pass the data we want to share between our routes. Then in each route, we can pass the result of our createSharedData
call as a prop to the route's sharedData
property. The shared data props will then be stored, served and cached only once per session and merged into the result of the routes getData
result at runtime!
// static.config.js
import { createSharedData } from 'react-static/node'
export default {
getRoutes: async () => {
const blogMenu = createSharedData(getMyLargeAndHeavyMenu())
return [
{
path: '/',
template: 'src/containers/Home',
},
{
path: '/blog',
template: 'src/containers/Docs',
sharedData: {
blogMenu, // `blogMenu` will now be available to this route via
// RouteData but will only be loaded once per session!
},
},
{
path: '/help',
template: 'src/containers/Help',
sharedData: {
blogMenu, // `blogMenu` will now be available to this route via
// RouteData but will only be loaded once per session!
},
},
]
},
}