Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: basic-features/layout の更新 #309

Merged
merged 3 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions docs/basic-features/layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
description: Learn how to share components and state between Next.js pages with Layouts.
---

# Layouts

<details open>
<summary><b>Examples</b></summary>
<ul>
<li><a href="https://github.com/vercel/next.js/tree/canary/examples/layout-component">layout-component</a></li>
</ul>
</details>

The React model allows us to deconstruct a [page](/docs/basic-features/pages.md) into a series of components. Many of these components are often reused between pages. For example, you might have the same navigation bar and footer on every page.

```jsx
// components/layout.js

import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
```

## Examples

### Single Shared Layout with Custom App

If you only have one layout for your entire application, you can create a [Custom App](/docs/advanced-features/custom-app.md) and wrap your application with the layout. Since the `<Layout />` component is re-used when changing pages, its component state will be preserved (e.g. input values).

```jsx
// pages/_app.js

import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
```

### Per-Page Layouts

If you need multiple layouts, you can add a property `getLayout` to your page, allowing you to return a React component for the layout. This allows you to define the layout on a _per-page basis_. Since we're returning a function, we can have complex nested layouts if desired.

```jsx
// pages/index.js

import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
return {
/** Your content */
}
}

Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
```

```jsx
// pages/_app.js

export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)

return getLayout(<Component {...pageProps} />)
}
```

When navigating between pages, we want to *persist* page state (input values, scroll position, etc.) for a Single-Page Application (SPA) experience.

This layout pattern enables state persistence because the React component tree is maintained between page transitions. With the component tree, React can understand which elements have changed to preserve state.

> **Note**: This process is called [reconciliation](https://reactjs.org/docs/reconciliation.html), which is how React understands which elements have changed.

### With TypeScript

When using TypeScript, you must first create a new type for your pages which includes a `getLayout` function. Then, you must create a new type for your `AppProps` which overrides the `Component` property to use the previously created type.

```tsx
// pages/index.tsx

import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
return {
/** Your content */
}
}

Page.getLayout = function getLayout(page: ReactElement) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
```

```tsx
// pages/_app.tsx

import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'

type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page)

return getLayout(<Component {...pageProps} />)
}
```

### Data Fetching

Inside your layout, you can fetch data on the client-side using `useEffect` or a library like [SWR](https://swr.vercel.app/). Because this file is not a [Page](/docs/basic-features/pages.md), you cannot use `getStaticProps` or `getServerSideProps` currently.

```jsx
// components/layout.js

import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
const { data, error } = useSWR('/api/navigation', fetcher)

if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>

return (
<>
<Navbar links={data.links} />
<main>{children}</main>
<Footer />
</>
)
}
```

For more information on what to do next, we recommend the following sections:

<div class="card">
<a href="/docs/basic-features/pages.md">
<b>Pages:</b>
<small>Learn more about what pages are in Next.js.</small>
</a>
</div>

<div class="card">
<a href="/docs/advanced-features/custom-app.md">
<b>Custom App:</b>
<small>Learn more about how Next.js initialize pages.</small>
</a>
</div>
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
"title": "CSS のビルトインサポート",
"path": "/docs/basic-features/built-in-css-support.md"
},
{
"title": "Layouts",
"path": "/docs/basic-features/layouts.md"
},
{
"title": "画像最適化",
"path": "/docs/basic-features/image-optimization.md"
Expand Down