In next.js we use _app.js for layouts which should not be rerendered on page transitions. In this post we will see how we can extend this approach to persistent sublayouts on sections of our website.
In next.js we use the _app.js file to create layouts which do not need to be rerendered between page transitions.
_app.js
export default (props) => ( <div> <div>Header</div> {props.children} </div> )
This will include our header in all of our pages, without having to need to rerender the header when we move from one page to another.
For most cases this is all we need, but sometimes we face situations where we have subsections of our sites with additional layout parts, that should not be rerendered.
Imagine we create an account section for our page. Our existing pages would now be:
/pages/index.js /pages/account/index.js /pages/account/profile.js /pages/account/subscription.js
And we want to add a persistent side-bar to all our account pages:
Header ------------------------------------------ Account | * Profile | * Subscription | Page Content | |
How can we achieve this? We only have one
_app.js
file and we certainly wouldn't want to show the sidebar on /index.js
.
Luckily there is still a way to make use of _app.js
and to create a sidebar which is kept between page transitions.
Let's first create the sublayout:
/components/AccountLayout.js
import React, { useEffect } from 'react' import Link from 'next/link' const AccountLayout = (props) => { return ( <div style={{ display: 'flex', flexDirection: 'row' }}> <div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}> <Link href="/account"> <a>Account</a> </Link> <ul> <li> <Link href="/account/profile"> <a>Profile</a> </Link> </li> <li> <Link href="/account/subscription"> <a>Subscription</a> </Link> </li> </ul> </div> <div style={{ flex: 3 }}>{props.children}</div> </div> ) } export default AccountLayout
On components with a sublayout we can set the layout as a property:
/pages/account/index.js
import React from 'react' import AccountLayout from '../../componenets/AccountLayout' const Account = (props) => { return <div>AccountPage</div> } export default Account Account.Layout = AccountLayout
(We also need to set the
.Layout
property in /pages/account/profile.js
and /pages/account/subscription.js
.)
Now we are able to check in
_app.js
if the component we are rendering contains a layout. If it does, we wrap it with its Layout:
import Header from '../componenets/Header' function MyApp({ Component, pageProps }) { const wrappedComponent = Component.Layout ? ( <Component.Layout> <Component {...pageProps} /> </Component.Layout> ) : ( <Component {...pageProps} /> ) return ( <div> <Header /> {wrappedComponent} </div> ) } export default MyApp
If we now transition between two pages with the same Layout, the Layout is not unmounted and mounted again.