diff --git a/examples/react-server-components/.gitignore b/examples/react-server-components/.gitignore new file mode 100644 index 0000000000000..1437c53f70bc2 --- /dev/null +++ b/examples/react-server-components/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/examples/react-server-components/README.md b/examples/react-server-components/README.md new file mode 100644 index 0000000000000..1f8a6d8ff7d3f --- /dev/null +++ b/examples/react-server-components/README.md @@ -0,0 +1,35 @@ +# React Server Components (Alpha) + +This examples shows a simple Next.js application using React Server Components. + +Server Components are a new feature in React that let 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. + +Check out the documentation to learn more: + +- [React 18](https://nextjs.org/docs/advanced-features/react-18/overview) +- [Streaming SSR](https://nextjs.org/docs/advanced-features/react-18/streaming) +- [Server Components](https://nextjs.org/docs/advanced-features/react-18/server-components) + +## Preview + +Preview the example live on [StackBlitz](http://stackblitz.com/): + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/react-server-components) + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/react-server-components&project-name=react-server-components&repository-name=react-server-components) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: + +```bash +npx create-next-app --example react-server-components rsc-nextjs +# or +yarn create next-app --example react-server-components rsc-nextjs +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/react-server-components/components/content.client.js b/examples/react-server-components/components/content.client.js new file mode 100644 index 0000000000000..5dad0d2775a6e --- /dev/null +++ b/examples/react-server-components/components/content.client.js @@ -0,0 +1,11 @@ +export default function Content() { + return ( +
+ Server Components are a new feature in React that let 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. +
+ ) +} diff --git a/examples/react-server-components/components/profile.server.js b/examples/react-server-components/components/profile.server.js new file mode 100644 index 0000000000000..de1aab001f98a --- /dev/null +++ b/examples/react-server-components/components/profile.server.js @@ -0,0 +1,14 @@ +import { useData } from '../lib/use-data' + +const url = `https://api.github.com/repos/vercel/next.js` + +export default function Profile() { + const data = useData('star', () => fetch(url).then((r) => r.json())) + + return ( ++ Next.js: + {data['stargazers_count']} +
+ ) +} diff --git a/examples/react-server-components/lib/use-data.js b/examples/react-server-components/lib/use-data.js new file mode 100644 index 0000000000000..9b7eb59f4deeb --- /dev/null +++ b/examples/react-server-components/lib/use-data.js @@ -0,0 +1,55 @@ +const cache = {} + +// This is temporary and will eventually be replaced +// with react-fetch +export function useData(key, fetcher, opts = {}) { + const now = Date.now() + function mutate() { + cache[key].isValidating = true + return fetcher(key).then( + (r) => { + cache[key].isValidating = false + cache[key].timestamp = Date.now() + cache[key].data = r + return r + }, + (err) => { + cache[key].isValidating = false + console.error(err) + } + ) + } + + const createFetcher = () => () => { + const { data, isValidating, promise } = cache[key] + if (data !== undefined && !isValidating) { + return data + } + if (!promise) { + cache[key].promise = mutate() + } + throw cache[key].promise + } + + if (!cache[key]) { + cache[key] = { + data: undefined, + promise: null, + timestamp: 0, + isValidating: false, + } + cache[key].fn = createFetcher() + } else { + if (opts.revalidate) { + const timeDiff = now - cache[key].timestamp + + // revalidate + if (timeDiff > opts.revalidate * 1000) { + cache[key].data = undefined + cache[key].promise = undefined + } + } + } + + return cache[key].fn() +} diff --git a/examples/react-server-components/next.config.js b/examples/react-server-components/next.config.js new file mode 100644 index 0000000000000..d6e15f30608da --- /dev/null +++ b/examples/react-server-components/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + experimental: { + runtime: 'nodejs', + serverComponents: true, + }, +} diff --git a/examples/react-server-components/package.json b/examples/react-server-components/package.json new file mode 100644 index 0000000000000..efddc0edfc3e3 --- /dev/null +++ b/examples/react-server-components/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "canary", + "react": "18.0.0-rc.0", + "react-dom": "18.0.0-rc.0" + } +} diff --git a/examples/react-server-components/pages/api/repo.js b/examples/react-server-components/pages/api/repo.js new file mode 100644 index 0000000000000..78c1e24a968b1 --- /dev/null +++ b/examples/react-server-components/pages/api/repo.js @@ -0,0 +1,6 @@ +export default async function handler(req, res) { + const response = await fetch('https://api.github.com/repos/vercel/next.js') + const repo = await response.json() + + return res.json(repo) +} diff --git a/examples/react-server-components/pages/index.server.js b/examples/react-server-components/pages/index.server.js new file mode 100644 index 0000000000000..a48a0305eb9a7 --- /dev/null +++ b/examples/react-server-components/pages/index.server.js @@ -0,0 +1,15 @@ +import { Suspense } from 'react' +import Profile from '../components/profile.server' +import Content from '../components/content.client' + +export default function Home() { + return ( +