Next.js getInitialProps in All Components
Make getInitialProps available to child components in Next.js
What is getInitialProps
Next.js adds a great little function to top-level (page) components. It gives access to a number of properties, such as the request and response objects and url information such as the pathname
. For the full list, see the readme.
This data is useful in more than just the top-level page components.
For All Pages
To get all pages to have the getInitialProps
data available, we could implement this function on every page. Or we could create a shared component that implements getInitialProps
in one place.
This component doesn't have any UI per se, so we also don't want to render any DOM. One way to accomplish this is to write a higher-order component that wraps the top level component of each page.
Such a component, with-server-props.js
, might look like this:
export default WrappedComponent =>
class extends React.Component {
static async getInitialProps({ pathname }) {
return { pathname }
}
render() {
return <WrappedComponent pathname={this.props.pathname} />
}
}
This will make the pathname
prop available to each page.
For All Child Components
But what if we want to make pathname
or other bits of data that getInitialProps
can provide available to any other component?
We have a couple of options. We could:
- Drill props for
pathname
, etc. all the way down to where they're needed. - Our server component wrapper could be modified to push this data into a shared data structure, a la redux store.
- We could avoid drilling or another library and put
pathname
, etc into React context.
Any one of these or other options would probably work fine depending on your use case and preference.
Let's share this data with React context. To start, we modify our page component wrapper. The component transfers pathname
from props to context via the getChildContext
function:
import PropTypes from 'prop-types'
export default WrappedComponent => {
class WithServerProps extends React.Component {
static async getInitialProps({ pathname }) {
return { pathname }
}
getChildContext() {
return { pathname: this.props.pathname }
}
render() {
return <WrappedComponent />
}
}
WithServerProps.childContextTypes = {
pathname: PropTypes.string.isRequired
}
return WithServerProps
}
We would wrap a page, such as pages/index.js
, with our component:
import withServerProps from '../ui/with-server-props'
import ChildComponent from '../ui/child-component'
export default withServerProps(_ =>
<div>
etc
<ChildComponent />
</div>
)
Then ChildComponent
or any similar non-root node component can get the pathname
from context:
class ChildComponent extends React.Component {
render() { return <div>{this.context.pathname}</div> }
}
ChildComponent.contextTypes = {
pathname: PropTypes.string
}
export default ChildComponent
Now getInitialProps
is effectively available everywhere. Just adjust contextTypes
in the child component to determine what you want to pull out of context.
How does this approach strike you? Are there better options? How do you make getInitialProps
data available to your components in Next.js?