Skip to content

Commit

Permalink
Examples: Jotai (#27940)
Browse files Browse the repository at this point in the history
## Documentation / Examples

- [X] Make sure the linting passes

This adds an example using Jotai with Next.js. It uses the new hook `useHydrateAtoms` to hydrate atoms with values from the server.
  • Loading branch information
Thisen committed Aug 14, 2021
1 parent d57d9a5 commit 8a80b0b
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 0 deletions.
34 changes: 34 additions & 0 deletions 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
31 changes: 31 additions & 0 deletions 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)).
3 changes: 3 additions & 0 deletions examples/with-jotai/next-env.d.ts
@@ -0,0 +1,3 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
3 changes: 3 additions & 0 deletions examples/with-jotai/next.config.js
@@ -0,0 +1,3 @@
module.exports = {
reactStrictMode: true,
}
22 changes: 22 additions & 0 deletions 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"
}
6 changes: 6 additions & 0 deletions 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 <Component {...pageProps} />
}
13 changes: 13 additions & 0 deletions 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<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
50 changes: 50 additions & 0 deletions 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<typeof getServerSideProps>) {
useHydrateAtoms([[countAtom, initialCount]] as const)
const [count, setCount] = useAtom(countAtom)
const doubleCount = useAtomValue(doubleAtom)
return (
<div className={styles.container}>
<Head>
<title>with-jotai</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1 className={styles.title}>With Jotai example</h1>
<main className={styles.main}>
<div>Initial count from the server: {count}</div>
<div>Double count: {doubleCount}</div>
<button onClick={() => setCount((count) => count + 1)}>+1</button>
</main>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
)
}
Binary file added examples/with-jotai/public/favicon.ico
Binary file not shown.
4 changes: 4 additions & 0 deletions examples/with-jotai/public/vercel.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 121 additions & 0 deletions 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;
}
}
16 changes: 16 additions & 0 deletions 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;
}
19 changes: 19 additions & 0 deletions 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"]
}

0 comments on commit 8a80b0b

Please sign in to comment.