When building projects with Next.js, we typically create an entire user interface by assembling isolated components. However, some parts of the interface require the same code snippets across multiple routes — for example, the navigation header, footer, and sidebar. To manage this, we use layouts to structure the interface in a way that contains shared code snippets. Next.js recommends starting a new project with the App Router. However, we’ll discuss how to implement layouts and nested layouts with the Pages Router for users who have yet to migrate to the new Next.js routing system.
To help illustrate the differences between the two approaches, we’ll create the same application using both methods and compare how the new App Router simplifies the process of implementing nested layouts.
To start, let’s take a look at a typical folder structure for the Pages Router in Next.js:
...
├── components
│ ├── Footer.js
│ └── Header.js
├── pages
│ ├── dashboard
│ │ ├── account.js
│ │ ├── analytics.js
│ │ └── settings.js
│ ...
│ ├── index.js
│ └── newsletter.js
...
To define a layout with the Pages routing system, we create a Layout component that renders any shared user interface and its children.
Create a components/Layout.js file and render the shared header and footer content:
import Header from './Header';
import Footer from './Footer';
const RootLayout = ({ children }) => {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
);
};
export default RootLayout;
The Layout component takes a children prop that serves as a placeholder for the active page content.
const RootLayout = ({ children }) => {
return (
<div className="flex flex-col min-h-screen mx-auto max-w-2xl px-4 pt-8 pb-16">
<div className="flex-grow">
<Header />
<main className="my-0 py-16">{children}</main>
</div>
<Footer />
</div>
);
};
However, this implementation doesn’t preserve the state between page transitions. For example, the search field’s input text gets cleared when navigating between pages that share a common layout. This isn’t the experience we expect from a single-page application.
If we examine the pages/_app.js file that Next.js calls during each page initialization, we’ll see an App component that includes a Component prop representing the active page:
import '@/styles/globals.css'
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
In this file, we can load the shared layout and other global files, such as the global CSS file. So, let’s wrap the page content with the <RootLayout> like so:
import '@/styles/globals.css';
import RootLayout from '@/components/Layout';
export default function App({ Component, pageProps }) {
return (
<RootLayout>
<Component {...pageProps} />
</RootLayout>
);
}
With this implementation, the RootLayout component is reused between page transitions. As a result, the state in the shared component, such as the Header, will be preserved.
We no longer need to wrap each page’s render with the <RootLayout> component.
After saving all files and revisiting the application, we can write in the search field and see that the state now persists between page changes, which is an improvement!
To create a nested shared layout, as demonstrated in the /dashboard/* pages, we need to nest a new layout that renders the sidebar navigation within the root layout.
However, with the current implementation, simply wrapping the active route within the root layout — as we did in the pages/_app.js file — only works if we require one layout for the entire application.
How to Parse JSON in Dart/Flutter Parsing JSON is a very common task for apps that need to fetch data from the Internet. And depending on how much JSON data you need to process, you have two options: write all the JSON parsing code manually automate the process with code generation Encoding and Decoding JSON When a JSON response is sent over the network, the entire payload is encoded as a string. But inside our Flutter apps, we don't want to extract the data from a string manually:
Introducing the useLoaderData Hook: The useLoaderData hook is a custom hook in React that helps you load data into your component. It simplifies the process of fetching data from an API or performing any asynchronous operation. When you use the useLoaderData hook, you provide it with a function that returns a Promise. This Promise represents an asynchronous operation that will fetch the data you need. Once the Promise resolves, the data becomes available to your component.