diff --git a/examples/with-jotai/.gitignore b/examples/with-jotai/.gitignore new file mode 100644 index 000000000000000..1437c53f70bc211 --- /dev/null +++ b/examples/with-jotai/.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/with-jotai/README.md b/examples/with-jotai/README.md new file mode 100644 index 000000000000000..cae5e90be62791f --- /dev/null +++ b/examples/with-jotai/README.md @@ -0,0 +1,31 @@ +# Jotai example + +This example shows how to integrate [Jotai](https://github.com/pmndrs/jotai) in Next.js. + +- Jotai is a primitive and flexible state management library for React. +- Jotai is TypeScript oriented and aims to expose a minimalistic API for dealing with state in a data-flow graph way. +- Jotai uses the `useHydrateAtoms` hook to hydrate the value of an atom, for values that come from the server. + +## 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/with-jotai) + +## 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/with-jotai&project-name=with-jotai&repository-name=with-jotai) + +## 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 with-jotai with-jotai-app +# or +yarn create next-app --example with-jotai with-jotai-app +``` + +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/with-jotai/next-env.d.ts b/examples/with-jotai/next-env.d.ts new file mode 100644 index 000000000000000..c6643fda12ff6f8 --- /dev/null +++ b/examples/with-jotai/next-env.d.ts @@ -0,0 +1,3 @@ +/// +/// +/// diff --git a/examples/with-jotai/next.config.js b/examples/with-jotai/next.config.js new file mode 100644 index 000000000000000..0d6071006ab351f --- /dev/null +++ b/examples/with-jotai/next.config.js @@ -0,0 +1,3 @@ +module.exports = { + reactStrictMode: true, +} diff --git a/examples/with-jotai/package.json b/examples/with-jotai/package.json new file mode 100644 index 000000000000000..5392fc044083af9 --- /dev/null +++ b/examples/with-jotai/package.json @@ -0,0 +1,22 @@ +{ + "name": "with-jotai", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "jotai": "1.3.0", + "next": "latest", + "react": "17.0.2", + "react-dom": "17.0.2" + }, + "devDependencies": { + "@types/react": "17.0.16", + "eslint": "7.32.0", + "eslint-config-next": "11.0.1", + "typescript": "4.3.5" + }, + "license": "MIT" +} diff --git a/examples/with-jotai/pages/_app.tsx b/examples/with-jotai/pages/_app.tsx new file mode 100644 index 000000000000000..33da2fce548ca16 --- /dev/null +++ b/examples/with-jotai/pages/_app.tsx @@ -0,0 +1,6 @@ +import '../styles/globals.css' +import type { AppProps } from 'next/app' + +export default function MyApp({ Component, pageProps }: AppProps) { + return +} diff --git a/examples/with-jotai/pages/api/hello.ts b/examples/with-jotai/pages/api/hello.ts new file mode 100644 index 000000000000000..f8bcc7e5caed177 --- /dev/null +++ b/examples/with-jotai/pages/api/hello.ts @@ -0,0 +1,13 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from 'next' + +type Data = { + name: string +} + +export default function handler( + req: NextApiRequest, + res: NextApiResponse +) { + res.status(200).json({ name: 'John Doe' }) +} diff --git a/examples/with-jotai/pages/index.tsx b/examples/with-jotai/pages/index.tsx new file mode 100644 index 000000000000000..60e8f5138ba6a90 --- /dev/null +++ b/examples/with-jotai/pages/index.tsx @@ -0,0 +1,50 @@ +import { GetServerSideProps, InferGetServerSidePropsType } from 'next' +import Head from 'next/head' +import Image from 'next/image' +import styles from '../styles/Home.module.css' +import { atom, useAtom } from 'jotai' +import { useAtomValue, useHydrateAtoms } from 'jotai/utils' + +const countAtom = atom(0) +const doubleAtom = atom((get) => get(countAtom) * 2) + +export const getServerSideProps: GetServerSideProps<{ + initialCount: number +}> = async (context) => { + return { props: { initialCount: 42 } } +} + +export default function Home({ + initialCount, +}: InferGetServerSidePropsType) { + useHydrateAtoms([[countAtom, initialCount]] as const) + const [count, setCount] = useAtom(countAtom) + const doubleCount = useAtomValue(doubleAtom) + return ( +
+ + with-jotai + + + +

With Jotai example

+
+
Initial count from the server: {count}
+
Double count: {doubleCount}
+ +
+ +
+ ) +} diff --git a/examples/with-jotai/public/favicon.ico b/examples/with-jotai/public/favicon.ico new file mode 100644 index 000000000000000..718d6fea4835ec2 Binary files /dev/null and b/examples/with-jotai/public/favicon.ico differ diff --git a/examples/with-jotai/public/vercel.svg b/examples/with-jotai/public/vercel.svg new file mode 100644 index 000000000000000..fbf0e25a651c289 --- /dev/null +++ b/examples/with-jotai/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/examples/with-jotai/styles/Home.module.css b/examples/with-jotai/styles/Home.module.css new file mode 100644 index 000000000000000..35454bb748190db --- /dev/null +++ b/examples/with-jotai/styles/Home.module.css @@ -0,0 +1,121 @@ +.container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; +} + +.main { + padding: 5rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.footer { + width: 100%; + height: 100px; + border-top: 1px solid #eaeaea; + display: flex; + justify-content: center; + align-items: center; +} + +.footer a { + display: flex; + justify-content: center; + align-items: center; + flex-grow: 1; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin: 0; + line-height: 1.15; + font-size: 4rem; +} + +.title, +.description { + text-align: center; +} + +.description { + line-height: 1.5; + font-size: 1.5rem; +} + +.code { + background: #fafafa; + border-radius: 5px; + padding: 0.75rem; + font-size: 1.1rem; + font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, + Bitstream Vera Sans Mono, Courier New, monospace; +} + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + max-width: 800px; + margin-top: 3rem; +} + +.card { + margin: 1rem; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: color 0.15s ease, border-color 0.15s ease; + width: 45%; +} + +.card:hover, +.card:focus, +.card:active { + color: #0070f3; + border-color: #0070f3; +} + +.card h2 { + margin: 0 0 1rem 0; + font-size: 1.5rem; +} + +.card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; +} + +.logo { + height: 1em; + margin-left: 0.5rem; +} + +@media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } +} diff --git a/examples/with-jotai/styles/globals.css b/examples/with-jotai/styles/globals.css new file mode 100644 index 000000000000000..e5e2dcc23baf192 --- /dev/null +++ b/examples/with-jotai/styles/globals.css @@ -0,0 +1,16 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} diff --git a/examples/with-jotai/tsconfig.json b/examples/with-jotai/tsconfig.json new file mode 100644 index 000000000000000..4fa631c26142828 --- /dev/null +++ b/examples/with-jotai/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +}