diff --git a/contributing.md b/contributing.md index 6bfc13c554a6f..eb5c27aded797 100644 --- a/contributing.md +++ b/contributing.md @@ -3,7 +3,7 @@ Our Commitment to Open Source can be found [here](https://vercel.com/oss). 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device. -2. Create a new branch `git checkout -b MY_BRANCH_NAME` +2. Create a new branch: `git checkout -b MY_BRANCH_NAME` 3. Install yarn: `npm install -g yarn` 4. Install the dependencies: `yarn` 5. Run `yarn dev` to build and watch for code changes diff --git a/docs/migrating/from-create-react-app.md b/docs/migrating/from-create-react-app.md index 83a0eb481d7a1..09d5bbb1f1b37 100644 --- a/docs/migrating/from-create-react-app.md +++ b/docs/migrating/from-create-react-app.md @@ -199,12 +199,12 @@ export default function SEO({ description, title, siteTitle }) { ## Single-Page App (SPA) -If you want to move your existing Create React App to Next.js and keep a Single-Page App, you can move your old application entry point to an [Optional Catch-All Route](/docs/routing/dynamic-routes.md#optional-catch-all-routes) named `pages/[[…app]].js`. +If you want to move your existing Create React App to Next.js and keep a Single-Page App, you can move your old application's entry point to an [Optional Catch-All Route](/docs/routing/dynamic-routes.md#optional-catch-all-routes) named `pages/[[…app]].js`. ```jsx // pages/[[...app]].js -import { useState } from 'react' +import { useState, useEffect } from 'react' import CreateReactAppEntryPoint from '../components/app' function App() { diff --git a/examples/blog/.gitignore b/examples/blog/.gitignore new file mode 100644 index 0000000000000..18088234abe02 --- /dev/null +++ b/examples/blog/.gitignore @@ -0,0 +1,36 @@ +# 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 + +public/feed.xml \ No newline at end of file diff --git a/examples/blog/README.md b/examples/blog/README.md new file mode 100644 index 0000000000000..fb4ec38394c84 --- /dev/null +++ b/examples/blog/README.md @@ -0,0 +1,36 @@ +# Portfolio Starter Kit + +This portfolio is built with **Next.js** and a library called [Nextra](https://nextra.vercel.app/). It allows you to write Markdown and focus on the _content_ of your portfolio. This starter includes: + +- Automatically configured to handle Markdown/MDX +- Generates an RSS feed based on your posts +- A beautiful theme included out of the box +- Easily categorize posts with tags +- Fast, optimized web font loading + +https://demo.vercel.blog + +## Configuration + +1. Update your name in `theme.config.js` or change the footer. +1. Update your name and site URL for the RSS feed in `scripts/gen-rss.js`. +1. Update the meta tags in `pages/_document.js`. +1. Update the posts inside `pages/posts/*.md` with your own content. + +## 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/blog&project-name=portfolio&repository-name=portfolio) + +## 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 blog my-blog +# or +yarn create next-app --example blog my-blog +``` + +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/blog/next.config.js b/examples/blog/next.config.js new file mode 100644 index 0000000000000..499fb45b4ab1e --- /dev/null +++ b/examples/blog/next.config.js @@ -0,0 +1,2 @@ +const withNextra = require('nextra')('nextra-theme-blog', './theme.config.js') +module.exports = withNextra() diff --git a/examples/blog/package.json b/examples/blog/package.json new file mode 100644 index 0000000000000..947846a512b98 --- /dev/null +++ b/examples/blog/package.json @@ -0,0 +1,27 @@ +{ + "name": "portfolio", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "dev": "next", + "build": "node ./scripts/gen-rss.js && next build", + "start": "next start" + }, + "dependencies": { + "gray-matter": "^4.0.2", + "next": "latest", + "nextra": "^0.4.3", + "nextra-theme-blog": "^0.1.4", + "react": "^17.0.1", + "react-dom": "^17.0.1", + "rss": "^1.2.2" + }, + "prettier": { + "arrowParens": "always", + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "semi": false + } +} diff --git a/examples/blog/pages/_app.js b/examples/blog/pages/_app.js new file mode 100644 index 0000000000000..803a85e066426 --- /dev/null +++ b/examples/blog/pages/_app.js @@ -0,0 +1,27 @@ +import 'nextra-theme-blog/style.css' +import Head from 'next/head' + +import '../styles/main.css' + +export default function Nextra({ Component, pageProps }) { + return ( + <> + + + + + + + ) +} diff --git a/examples/blog/pages/_document.js b/examples/blog/pages/_document.js new file mode 100644 index 0000000000000..bd31c09abe7ed --- /dev/null +++ b/examples/blog/pages/_document.js @@ -0,0 +1,41 @@ +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + static async getInitialProps(ctx) { + const initialProps = await Document.getInitialProps(ctx) + return { ...initialProps } + } + + render() { + const meta = { + title: 'Next.js Blog Starter Kit', + description: 'Clone and deploy your own Next.js portfolio in minutes.', + image: + 'https://assets.vercel.com/image/upload/q_auto/front/vercel/dps.png' + } + + return ( + + + + + + + + + + + + + + + +
+ + + + ) + } +} + +export default MyDocument diff --git a/examples/blog/pages/index.mdx b/examples/blog/pages/index.mdx new file mode 100644 index 0000000000000..da48626824c8a --- /dev/null +++ b/examples/blog/pages/index.mdx @@ -0,0 +1,46 @@ +--- +type: page +title: About +date: 2021-03-19 +--- + +# Your Name + +Hey, I'm a Senior Software Engineer at Company. I enjoy working with Next.js and crafting beautiful front-end experiences. + +This portfolio is built with **Next.js** and a library called [Nextra](https://nextra.vercel.app/). It allows you to write Markdown and focus on the _content_ of your portfolio. + +[**Deploy your own**](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/blog&project-name=portfolio&repository-name=portfolio) in a few minutes. + +--- + +
+ Twitter [@yourname](https://twitter.com/yourname) +
+ GitHub [@yourname](https://github.com/yourname) +
+ Instagram [@yourname](https://instagram.com/yourname) +
+ Email your@name.com +
+ +
+ +### Experience + +- Senior Software Engineer at Company 2021– +- Software Engineer at Company, 2017–2021 +- Bachelor of Computer Science at Your University, 2013–2017 + +### Projects + +- [Next.js](https://nextjs.org) +- [Nextra](https://nextra.vercel.app/) +- [Vercel](http://vercel.com) + +## Skills + +- Next.js +- TypeScript +- Vercel +- CSS diff --git a/examples/blog/pages/photos.mdx b/examples/blog/pages/photos.mdx new file mode 100644 index 0000000000000..32bc27f7899f2 --- /dev/null +++ b/examples/blog/pages/photos.mdx @@ -0,0 +1,31 @@ +--- +type: page +title: Photos +date: 2021-03-18 +--- + +# Photos + +Here's some of my photography. + +import Image from 'next/image' + +Photo +[Unsplash ↗ ](https://unsplash.com/photos/WeYamle9fDM) + +Photo +[Unsplash ↗ ](https://unsplash.com/photos/ndN00KmbJ1c) diff --git a/examples/blog/pages/posts/index.md b/examples/blog/pages/posts/index.md new file mode 100644 index 0000000000000..e985ac7548874 --- /dev/null +++ b/examples/blog/pages/posts/index.md @@ -0,0 +1,7 @@ +--- +type: posts +title: Posts +date: 2021-03-18 +--- + +# Posts diff --git a/examples/blog/pages/posts/markdown.md b/examples/blog/pages/posts/markdown.md new file mode 100644 index 0000000000000..397e49809d89f --- /dev/null +++ b/examples/blog/pages/posts/markdown.md @@ -0,0 +1,99 @@ +--- +title: Markdown Examples +date: 2021/3/19 +description: View examples of all possible Markdown options. +tag: web development +author: You +--- + +# Markdown Examples + +## h2 Heading + +### h3 Heading + +#### h4 Heading + +##### h5 Heading + +###### h6 Heading + +## Emphasis + +**This is bold text** + +_This is italic text_ + +~~Strikethrough~~ + +## Blockquotes + +> Develop. Preview. Ship. – Vercel + +## Lists + +Unordered + +- Lorem ipsum dolor sit amet +- Consectetur adipiscing elit +- Integer molestie lorem at massa + +Ordered + +1. Lorem ipsum dolor sit amet +2. Consectetur adipiscing elit +3. Integer molestie lorem at massa + +## Code + +Inline `code` + +``` +export default function Nextra({ Component, pageProps }) { + return ( + <> + + + + + + + ) +} +``` + +## Tables + +| **Option** | **Description** | +| ---------- | --------------------------------------------------------------------------------------------------------------------------- | +| First | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. | +| Second | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. | +| Third | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. | + +## Links + +- [Next.js](https://nextjs.org) +- [Nextra](https://nextra.vercel.app/) +- [Vercel](http://vercel.com) + +### Footnotes + +- Footnote [^1]. +- Footnote [^2]. + +[^1]: Footnote **can have markup** + + and multiple paragraphs. + +[^2]: Footnote text. diff --git a/examples/blog/pages/posts/pages.md b/examples/blog/pages/posts/pages.md new file mode 100644 index 0000000000000..d97c0afefc3d8 --- /dev/null +++ b/examples/blog/pages/posts/pages.md @@ -0,0 +1,238 @@ +--- +title: Next.js Pages +date: 2021/3/18 +description: Learn more about Next.js pages. +tag: web development +author: You +--- + +# Next.js Pages + +In Next.js, a **page** is a [React Component](https://reactjs.org/docs/components-and-props.html) exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the `pages` directory. Each page is associated with a route based on its file name. + +**Example**: If you create `pages/about.js` that exports a React component like below, it will be accessible at `/about`. + +``` +function About() { + return
About
+} + +export default About +``` + +### Pages with Dynamic Routes + +Next.js supports pages with dynamic routes. For example, if you create a file called `pages/posts/[id].js`, then it will be accessible at `posts/1`, `posts/2`, etc. + +> To learn more about dynamic routing, check the [Dynamic Routing documentation](/docs/routing/dynamic-routes.md). + +## Pre-rendering + +By default, Next.js **pre-renders** every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript. Pre-rendering can result in better performance and SEO. + +Each generated HTML is associated with minimal JavaScript code necessary for that page. When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. (This process is called _hydration_.) + +### Two forms of Pre-rendering + +Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page. + +- [**Static Generation (Recommended)**](#static-generation-recommended): The HTML is generated at **build time** and will be reused on each request. +- [**Server-side Rendering**](#server-side-rendering): The HTML is generated on **each request**. + +Importantly, Next.js lets you **choose** which pre-rendering form you'd like to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others. + +We **recommend** using **Static Generation** over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN with no extra configuration to boost performance. However, in some cases, Server-side Rendering might be the only option. + +You can also use **Client-side Rendering** along with Static Generation or Server-side Rendering. That means some parts of a page can be rendered entirely by client side JavaScript. To learn more, take a look at the [Data Fetching](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side) documentation. + +## Static Generation (Recommended) + +If a page uses **Static Generation**, the page HTML is generated at **build time**. That means in production, the page HTML is generated when you run `next build` . This HTML will then be reused on each request. It can be cached by a CDN. + +In Next.js, you can statically generate pages **with or without data**. Let's take a look at each case. + +### Static Generation without data + +By default, Next.js pre-renders pages using Static Generation without fetching data. Here's an example: + +``` +function About() { + return
About
+} + +export default About +``` + +Note that this page does not need to fetch any external data to be pre-rendered. In cases like this, Next.js generates a single HTML file per page during build time. + +### Static Generation with data + +Some pages require fetching external data for pre-rendering. There are two scenarios, and one or both might apply. In each case, you can use a special function Next.js provides: + +1. Your page **content** depends on external data: Use `getStaticProps`. +2. Your page **paths** depend on external data: Use `getStaticPaths` (usually in addition to `getStaticProps`). + +#### Scenario 1: Your page **content** depends on external data + +**Example**: Your blog page might need to fetch the list of blog posts from a CMS (content management system). + +``` +// TODO: Need to fetch `posts` (by calling some API endpoint) +// before this page can be pre-rendered. +function Blog({ posts }) { + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ) +} + +export default Blog +``` + +To fetch this data on pre-render, Next.js allows you to `export` an `async` function called `getStaticProps` from the same file. This function gets called at build time and lets you pass fetched data to the page's `props` on pre-render. + +``` +function Blog({ posts }) { + // Render posts... +} + +// This function gets called at build time +export async function getStaticProps() { + // Call an external API endpoint to get posts + const res = await fetch('https://.../posts') + const posts = await res.json() + + // By returning { props: { posts } }, the Blog component + // will receive `posts` as a prop at build time + return { + props: { + posts + } + } +} + +export default Blog +``` + +To learn more about how `getStaticProps` works, check out the [Data Fetching documentation](/docs/basic-features/data-fetching.md#getstaticprops-static-generation). + +#### Scenario 2: Your page paths depend on external data + +Next.js allows you to create pages with **dynamic routes**. For example, you can create a file called `pages/posts/[id].js` to show a single blog post based on `id`. This will allow you to show a blog post with `id: 1` when you access `posts/1`. + +> To learn more about dynamic routing, check the [Dynamic Routing documentation](/docs/routing/dynamic-routes.md). + +However, which `id` you want to pre-render at build time might depend on external data. + +**Example**: suppose that you've only added one blog post (with `id: 1`) to the database. In this case, you'd only want to pre-render `posts/1` at build time. + +Later, you might add the second post with `id: 2`. Then you'd want to pre-render `posts/2` as well. + +So your page **paths** that are pre-rendered depend on external data**.** To handle this, Next.js lets you `export` an `async` function called `getStaticPaths` from a dynamic page (`pages/posts/[id].js` in this case). This function gets called at build time and lets you specify which paths you want to pre-render. + +``` +// This function gets called at build time +export async function getStaticPaths() { + // Call an external API endpoint to get posts + const res = await fetch('https://.../posts') + const posts = await res.json() + + // Get the paths we want to pre-render based on posts + const paths = posts.map((post) => ({ + params: { id: post.id } + })) + + // We'll pre-render only these paths at build time. + // { fallback: false } means other routes should 404. + return { paths, fallback: false } +} +``` + +Also in `pages/posts/[id].js`, you need to export `getStaticProps` so that you can fetch the data about the post with this `id` and use it to pre-render the page: + +``` +function Post({ post }) { + // Render post... +} + +export async function getStaticPaths() { + // ... +} + +// This also gets called at build time +export async function getStaticProps({ params }) { + // params contains the post `id`. + // If the route is like /posts/1, then params.id is 1 + const res = await fetch(`https://.../posts/${params.id}`) + const post = await res.json() + + // Pass post data to the page via props + return { props: { post } } +} + +export default Post +``` + +To learn more about how `getStaticPaths` works, check out the [Data Fetching documentation](/docs/basic-features/data-fetching.md#getstaticpaths-static-generation). + +### When should I use Static Generation? + +We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request. + +You can use Static Generation for many types of pages, including: + +- Marketing pages +- Blog posts +- E-commerce product listings +- Help and documentation + +You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation. + +On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request. + +In cases like this, you can do one of the following: + +- Use Static Generation with **Client-side Rendering:** You can skip pre-rendering some parts of a page and then use client-side JavaScript to populate them. To learn more about this approach, check out the [Data Fetching documentation](/docs/basic-features/data-fetching.md#fetching-data-on-the-client-side). +- Use **Server-Side Rendering:** Next.js pre-renders a page on each request. It will be slower because the page cannot be cached by a CDN, but the pre-rendered page will always be up-to-date. We'll talk about this approach below. + +## Server-side Rendering + +> Also referred to as "SSR" or "Dynamic Rendering". + +If a page uses **Server-side Rendering**, the page HTML is generated on **each request**. + +To use Server-side Rendering for a page, you need to `export` an `async` function called `getServerSideProps`. This function will be called by the server on every request. + +For example, suppose that your page needs to pre-render frequently updated data (fetched from an external API). You can write `getServerSideProps` which fetches this data and passes it to `Page` like below: + +``` +function Page({ data }) { + // Render data... +} + +// This gets called on every request +export async function getServerSideProps() { + // Fetch data from external API + const res = await fetch(`https://.../data`) + const data = await res.json() + + // Pass data to the page via props + return { props: { data } } +} + +export default Page +``` + +As you can see, `getServerSideProps` is similar to `getStaticProps`, but the difference is that `getServerSideProps` is run on every request instead of on build time. + +To learn more about how `getServerSideProps` works, check out our [Data Fetching documentation](/docs/basic-features/data-fetching.md#getserversideprops-server-side-rendering) + +## Summary + +We've discussed two forms of pre-rendering for Next.js. + +- **Static Generation (Recommended):** The HTML is generated at **build time** and will be reused on each request. To make a page use Static Generation, either export the page component, or export `getStaticProps` (and `getStaticPaths` if necessary). It's great for pages that can be pre-rendered ahead of a user's request. You can also use it with Client-side Rendering to bring in additional data. +- **Server-side Rendering:** The HTML is generated on **each request**. To make a page use Server-side Rendering, export `getServerSideProps`. Because Server-side Rendering results in slower performance than Static Generation, use this only if absolutely necessary. diff --git a/examples/blog/pages/tags/[tag].mdx b/examples/blog/pages/tags/[tag].mdx new file mode 100644 index 0000000000000..970952ce0cdac --- /dev/null +++ b/examples/blog/pages/tags/[tag].mdx @@ -0,0 +1,13 @@ +--- +type: tag +title: Tagged Posts +--- + +import { useRouter } from 'next/router' + +export const TagName = () => { + const { tag } = useRouter().query + return tag || null +} + +# Posts Tagged with “” diff --git a/examples/blog/public/favicon.ico b/examples/blog/public/favicon.ico new file mode 100644 index 0000000000000..4965832f2c9b0 Binary files /dev/null and b/examples/blog/public/favicon.ico differ diff --git a/examples/blog/public/fonts/Inter-italic.latin.var.woff2 b/examples/blog/public/fonts/Inter-italic.latin.var.woff2 new file mode 100644 index 0000000000000..5066cfdf36722 Binary files /dev/null and b/examples/blog/public/fonts/Inter-italic.latin.var.woff2 differ diff --git a/examples/blog/public/fonts/Inter-roman.latin.var.woff2 b/examples/blog/public/fonts/Inter-roman.latin.var.woff2 new file mode 100644 index 0000000000000..9503ba17ff104 Binary files /dev/null and b/examples/blog/public/fonts/Inter-roman.latin.var.woff2 differ diff --git a/examples/blog/public/images/photo.jpg b/examples/blog/public/images/photo.jpg new file mode 100644 index 0000000000000..449c181d86714 Binary files /dev/null and b/examples/blog/public/images/photo.jpg differ diff --git a/examples/blog/public/images/photo2.jpg b/examples/blog/public/images/photo2.jpg new file mode 100644 index 0000000000000..2b028af36a8a2 Binary files /dev/null and b/examples/blog/public/images/photo2.jpg differ diff --git a/examples/blog/scripts/gen-rss.js b/examples/blog/scripts/gen-rss.js new file mode 100644 index 0000000000000..1dba6d6495e50 --- /dev/null +++ b/examples/blog/scripts/gen-rss.js @@ -0,0 +1,38 @@ +const { promises: fs } = require('fs') +const path = require('path') +const RSS = require('rss') +const matter = require('gray-matter') + +async function generate() { + const feed = new RSS({ + title: 'Your Name', + site_url: 'https://yoursite.com', + feed_url: 'https://yoursite.com/feed.xml' + }) + + const posts = await fs.readdir(path.join(__dirname, '..', 'pages', 'posts')) + + await Promise.all( + posts.map(async (name) => { + if (name.startsWith('index.')) return + + const content = await fs.readFile( + path.join(__dirname, '..', 'pages', 'posts', name) + ) + const frontmatter = matter(content) + + feed.item({ + title: frontmatter.data.title, + url: '/posts/' + name.replace(/\.mdx?/, ''), + date: frontmatter.data.date, + description: frontmatter.data.description, + categories: frontmatter.data.tag.split(', '), + author: frontmatter.data.author + }) + }) + ) + + await fs.writeFile('./public/feed.xml', feed.xml({ indent: true })) +} + +generate() diff --git a/examples/blog/styles/main.css b/examples/blog/styles/main.css new file mode 100644 index 0000000000000..d5016fa9a6462 --- /dev/null +++ b/examples/blog/styles/main.css @@ -0,0 +1,57 @@ +@font-face { + font-family: 'Inter var'; + font-style: normal; + font-weight: 100 900; + font-display: block; + src: url(/fonts/Inter-roman.latin.var.woff2) format('woff2'); +} +@font-face { + font-family: 'Inter var'; + font-style: italic; + font-weight: 100 900; + font-display: block; + src: url(/fonts/Inter-italic.latin.var.woff2) format('woff2'); + font-named-instance: 'Italic'; +} + +body { + font-family: 'Inter var', system-ui, -apple-system, BlinkMacSystemFont, + 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + -webkit-font-smoothing: subpixel-antialiased; + font-feature-settings: 'case' 1, 'cpsp' 1, 'dlig' 1, 'cv01' 1, 'cv02', + 'cv03' 1, 'cv04' 1; + font-variation-settings: 'wght' 450; + font-variant: common-ligatures contextual; + letter-spacing: -0.02em; +} +b, +strong, +h3, +h4, +h5, +h6 { + font-variation-settings: 'wght' 650; +} +h1 { + font-variation-settings: 'wght' 850; +} +h2 { + font-variation-settings: 'wght' 750; +} + +@media screen and (min-device-pixel-ratio: 1.5), + screen and (min-resolution: 1.5dppx) { + body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +} + +details summary { + cursor: pointer; +} + +img.next-image { + margin: 0; +} diff --git a/examples/blog/theme.config.js b/examples/blog/theme.config.js new file mode 100644 index 0000000000000..fb56fe005f6df --- /dev/null +++ b/examples/blog/theme.config.js @@ -0,0 +1,21 @@ +const YEAR = new Date().getFullYear() + +export default { + footer: ( + + © Your Name. + RSS + + + ) +} diff --git a/examples/with-redux-toolkit-typescript/.gitignore b/examples/with-redux-toolkit-typescript/.gitignore new file mode 100644 index 0000000000000..1437c53f70bc2 --- /dev/null +++ b/examples/with-redux-toolkit-typescript/.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-redux-toolkit-typescript/README.md b/examples/with-redux-toolkit-typescript/README.md new file mode 100644 index 0000000000000..20599f991daf6 --- /dev/null +++ b/examples/with-redux-toolkit-typescript/README.md @@ -0,0 +1,23 @@ +# Redux Toolkit TypeScript Example + +This example shows how to integrate Next.js with [Redux Toolkit](https://redux-toolkit.js.org). + +The **Redux Toolkit** is intended to be the standard way to write Redux logic (create actions and reducers, setup the store with some default middlewares like redux devtools extension). This example demonstrates each of these features with Next.js + +## 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-redux-toolkit-typescript&project-name=with-redux-toolkit&repository-name=with-redux-toolkit) + +## 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-redux-toolkit-typescript with-redux-toolkit-app +# or +yarn create next-app --example with-redux-toolkit-typescript with-redux-toolkit-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-redux-toolkit-typescript/components/add-note.tsx b/examples/with-redux-toolkit-typescript/components/add-note.tsx new file mode 100644 index 0000000000000..c06d35bd43b3d --- /dev/null +++ b/examples/with-redux-toolkit-typescript/components/add-note.tsx @@ -0,0 +1,41 @@ +import { FC } from 'react' +import { useDispatch, useSelector } from 'react-redux' + +import { addNote, selectNotes } from '../lib/slices/notesSlice' +import useForm from '../lib/useForm' +import { Note } from '../types/Note' +import { isErrorResponse } from '../types/ErrorResponse' + +const AddNoteForm: FC = () => { + const dispatch = useDispatch() + const { error } = useSelector(selectNotes) + const handleSubmit = useForm({ + title: '', + content: '', + }) + + return ( +
dispatch(addNote(data)))}> +

Create a Note

+ +
+ {isErrorResponse(error) && {error.title}} +
+