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 ( +
+

Welcome to React Server Components

+ + + + +
+ ) +} diff --git a/yarn.lock b/yarn.lock index f557f48cfb938..448b0971b07e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20710,7 +20710,7 @@ watchpack-chokidar2@^2.0.0: dependencies: chokidar "^2.1.8" -watchpack@2.3.1, watchpack@^2.3.1: +watchpack@2.3.1, watchpack@^2.3.0, watchpack@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== @@ -20812,8 +20812,7 @@ webpack-bundle-analyzer@4.3.0: source-list-map "^2.0.0" source-map "~0.6.1" -"webpack-sources3@npm:webpack-sources@3.2.3", webpack-sources@^3.2.3: - name webpack-sources3 +"webpack-sources3@npm:webpack-sources@3.2.3", webpack-sources@^3.2.2, webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==