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

docs: react 18, streaming SSR, rsc with new apis #33986

Merged
merged 17 commits into from Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
154 changes: 0 additions & 154 deletions docs/advanced-features/react-18.md

This file was deleted.

27 changes: 27 additions & 0 deletions docs/advanced-features/react-18/basic.md
@@ -0,0 +1,27 @@
# React 18

[React 18](https://reactjs.org/blog/2021/06/08/the-plan-for-react-18.html) adds new features including Suspense, automatic batching of updates, APIs like `startTransition`, and a new streaming API for server rendering with support for `React.lazy`.
leerob marked this conversation as resolved.
Show resolved Hide resolved

React 18 is in RC now. Read more about React 18's [release plan](https://reactjs.org/blog/2021/06/08/the-plan-for-react-18.html) and discussions from the [working group](https://github.com/reactwg/react-18/discussions).
huozhi marked this conversation as resolved.
Show resolved Hide resolved

## Using React 18 with Next.js

Install the Release Candidate version of React 18:
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```jsx
npm install next@latest react@rc react-dom@rc
```

You can now start using React 18's new APIs like `startTransition` and `Suspense` in Next.js.

## Streaming SSR (Alpha)

Streaming server-rendering (SSR) is an experimental feature in Next.js 12. When enabled, SSR will use the same [Edge Runtime](/docs/api-reference/edge-runtime.md) as [Middleware](/docs/middleware.md).

[Learn how to enable streaming in Next.js.](/docs/react-18/streaming.md)

## React Server Components (Alpha)

Server Components are a new feature in React that lets you reduce your JavaScript bundle size by separating server and client-side code. Server Components allow developers to build apps that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

Server Components are still in research and development. [Learn how to try Server Components](/docs/react-18/server-components.md) as an experimental feature in Next.js.
124 changes: 124 additions & 0 deletions docs/advanced-features/react-18/server-components.md
@@ -0,0 +1,124 @@
# React Server Components (Alpha)

Server Components allow us to render React components on the server. This is fundamentally different from server-side rendering where you're pre-generating HTML on the server. With Server Components, there's **zero client-side JavaScript needed,** making page rendering faster. This improves the user experience of your application, pairing the best parts of server-rendering with client-side interactivity.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

### Enable React Server Components

To use React Server Components, ensure you have React 18 installed. Then, turn on the `concurrentFeatures` and `serverComponents` options in `next.config.js`:

```jsx
// next.config.js
module.exports = {
experimental: {
concurrentFeatures: true,
serverComponents: true,
},
}
```

Next, if you already have customized `pages/_document` component, you need to remove the `getInitialProps` static method and the `getServerSideProps` export if there’s any, otherwise it won't work with server components. If no custom Document component is provided, Next.js will fallback to a basic functional document component. Check [next/document support](#next/document) for more details.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

Then, you can start using React Server Components. [See our example](https://github.com/vercel/next-rsc-demo) for more information.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

### Server Components Convention
huozhi marked this conversation as resolved.
Show resolved Hide resolved

To run a component on the server, append `.server.js` to the end of the filename. For example `./pages/home.server.js` is a Server Component.
huozhi marked this conversation as resolved.
Show resolved Hide resolved
huozhi marked this conversation as resolved.
Show resolved Hide resolved

For client components, add `.client.js`. For example, `./components/avatar.client.js`.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

You can then import other server or client components from any server component. Note: a server component **can not** be imported by a client component. Components without "server/client" extensions will be treated as "universal component" and can be used and rendered by both sides, depending on where it is imported. For example:
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```jsx
// pages/home.server.js

import { Suspense } from 'react'

import Profile from '../components/profile.server'
import Content from '../components/content.client'

export default function Home() {
return (
<div>
<h1>Welcome to React Server Components</h1>
<Suspense fallback={'Loading...'}>
<Profile />
</Suspense>
<Content />
</div>
)
}
```

The `<Home>` and `<Profile>` components will always be server-side rendered and streamed to the client, and will not be included by the client runtime. However `<Content>` will still be hydrated on the client-side, like normal React components.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

To see a full example, check out [link to the demo and repository](https://github.com/vercel/next-rsc-demo).

huozhi marked this conversation as resolved.
Show resolved Hide resolved
## **Supported Next.js APIs**
huozhi marked this conversation as resolved.
Show resolved Hide resolved

### `next/link` and `next/image`

You could use `next/link` and `next/image` like before and they will be treated as client components to keep the interaction on client side.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

### `next/document`

If you have custom `_document` To use server components you have to change your `_document` to a functional component like below. Or if you don't have any, Next.js will provide a functional fallback `_document` component.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```jsx
// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
```

### `next/app`

If you're use `_app.js`, the usage is same with [Custom App](/docs/advanced-features/custom-app).
huozhi marked this conversation as resolved.
Show resolved Hide resolved
If you're use `_app.server.js` as a server component, the signature is changed as below where it only receive the `children` prop as element. You can surly wrap any other client or server components around `children` to customize the layout of your app.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```js
// pages/_app.server.js
export default function App({ children }) {
return children
}
```

### Routing

Basic routes with path and queries, dynamic routes are supported as well. If you need to access the router in server components(`.server.js`), They will receive `router` instance as a prop so that you could directly accessing them without using `useRouter()` hook.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```jsx
// pages/index.server.js

export default function Index({ router }) {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
// You can access routing information by `router.pathname`, etc.
return 'hello'
}
```

### **Unsupported Next.js APIs**
huozhi marked this conversation as resolved.
Show resolved Hide resolved

While RSC and SSR streaming is still in the alpha stage, not all Next.js APIs are supported. The following Next.js APIs have limited functionality within Server Components. Note that React 18 without SSR streaming isn't affected.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

#### React internals

Most of React hooks such as `useContext`, `useState`, `useReducer`, `useEffect` and `useLayoutEffect` are not supported as of today since Server Components are executed per requests and aren't stateful.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

#### Data Fetching & Styling

Like streaming SSR, styling and data fetching within `Suspense` on server side are not well supported. We're still working on them.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

Page level exported methods like `getInitialProps`, `getStaticProps` and `getStaticPaths` are not supported.

#### `next/head` and I18n

We're still working on them!
leerob marked this conversation as resolved.
Show resolved Hide resolved
62 changes: 62 additions & 0 deletions docs/advanced-features/react-18/streaming.md
@@ -0,0 +1,62 @@
# Streaming SSR (Alpha)

React 18 will include architectural improvements to React server-side rendering (SSR) performance. This means you could use `Suspense` in your React components in streaming SSR mode and React will send render them and send through HTTP streams. React server components is also an experimental feature based on streaming. Next.js will also provide server components related features along with other streaming support through `next/streaming`.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

## Enable Streaming SSR
huozhi marked this conversation as resolved.
Show resolved Hide resolved

To enable, use the experimental flag `concurrentFeatures: true`:
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```jsx
// next.config.js
module.exports = {
experimental: {
concurrentFeatures: true,
},
}
```

## Streaming Features

### next/dynamic

Using Dynamic import on React components by `React.lazy` have better support in React 18. Previously Next.js support dynamic import internally without require `Suspense` or `React.lazy`, Now to embrace the official APIs on React side we provide you with `options.suspense` in `next/dynamic`.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

```jsx
import dynamic from 'next/dynamic'
import { lazy, Suspense } from 'react'

import Content from '../components/content'

// These two ways are identical:
huozhi marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly identical, as React.lazy doesn't do any preloading. Might be worth suggesting that you continue using next/dynamic for now for performance?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we enable { suspense: true } it's using React.lazy and doesn't do preloading I think?

Copy link
Contributor

@devknoll devknoll Feb 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see the issue -- we just haven't implemented the preloading yet (since it happens after the shell is flushed, where all of the existing preloading is done today), but we should. Otherwise there's no reason to have the { suspense: true } option instead of just recommending that people use React.lazy instead.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The need for {suspense: true} makes it feel second-class. Do we plan to flip the default at some point in the future?

Copy link
Contributor

@devknoll devknoll Feb 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that currently that would be a breaking change without an incremental upgrade path: your application using next/dynamic would break when upgrading to React 18 unless you add suspense boundaries first, but you can't add suspense boundaries until you upgrade to React 18. It also changes the semantics a bit, since pre-Suspense, the loading state is associated with the next/dynamic component, rather than the parent where the component is rendered.

Changing the default in the future is a possibility, but it's likely a major version away. Some shorter term options might be:

  • If we could detect that we're rendering inside a <Suspense> boundary, we could assume you want the suspense behavior
  • If we could somehow port the preloading behavior to React.lazy, we could push users toward that instead, when using React>=18

Both of these options would probably require some support in React though.

Copy link
Contributor

@devknoll devknoll Feb 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It occurred to me that with facebook/react#23304 and facebook/react#23267 this might not actually be a breaking change anymore, if the transition semantics apply to root.render() as well... Would need to investigate further though.

Copy link
Contributor

@devknoll devknoll Feb 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what we could theoretically do is use suspense by default during SSR and initial hydration if you're using React>=18, since that behavior shouldn't change (after the next RC).

For client-side navigations that would still be at risk of suspending, we could synchronously render the legacy non-Suspense based fallback that you provide with next/dynamic, and then also warn that you should add a suspense boundary and move your fallback there instead.

Let me think about it some more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed that facebook/react#23304 does in fact change the behavior for root.render(...) as long as it's not wrapped in flushSync. So yeah, it would probably make sense to make this change.

Thanks for flagging @gaearon

const Profile = dynamic(() => import('./profile'), { suspense: true })
const Footer = lazy(() => import('./footer'))

export default function Home() {
return (
<div>
<Suspense fallback={<Spinner />}>
{/* A component that uses Suspense-based */}
huozhi marked this conversation as resolved.
Show resolved Hide resolved
<Content />
</Suspense>
<Suspense fallback={<Spinner />}>
<Profile />
</Suspense>
<Suspense fallback={<Spinner />}>
<Footer />
</Suspense>
</div>
)
}
```

Checkout [next/streaming](/docs/api-reference/next/streaming.md) for more details for building Next.js apps in streaming SSR mode.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

## Notice
huozhi marked this conversation as resolved.
Show resolved Hide resolved
huozhi marked this conversation as resolved.
Show resolved Hide resolved

#### Data Fetching

Currently data fetching within suspense boundaries on server side is not fully supported, which could lead to mismatching between server and client. In the short-term, please don't put data fetching within suspense.
huozhi marked this conversation as resolved.
Show resolved Hide resolved

#### Styling

Styling your components with styled-jsx or CSS modules are not supported well in streaming SSR. We're still working on them.
huozhi marked this conversation as resolved.
Show resolved Hide resolved