[examples] Add portfolio starter kit. (#23212)
This commit is contained in:
parent
89ec21ed68
commit
6a418b7da9
20 changed files with 719 additions and 0 deletions
36
examples/blog/.gitignore
vendored
Normal file
36
examples/blog/.gitignore
vendored
Normal file
|
@ -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
|
36
examples/blog/README.md
Normal file
36
examples/blog/README.md
Normal file
|
@ -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)).
|
2
examples/blog/next.config.js
Normal file
2
examples/blog/next.config.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const withNextra = require('nextra')('nextra-theme-blog', './theme.config.js')
|
||||
module.exports = withNextra()
|
27
examples/blog/package.json
Normal file
27
examples/blog/package.json
Normal file
|
@ -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
|
||||
}
|
||||
}
|
27
examples/blog/pages/_app.js
Normal file
27
examples/blog/pages/_app.js
Normal file
|
@ -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 (
|
||||
<>
|
||||
<Head>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="RSS"
|
||||
href="/feed.xml"
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Inter-roman.latin.var.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</>
|
||||
)
|
||||
}
|
41
examples/blog/pages/_document.js
Normal file
41
examples/blog/pages/_document.js
Normal file
|
@ -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 (
|
||||
<Html>
|
||||
<Head>
|
||||
<meta name="robots" content="follow, index" />
|
||||
<meta name="description" content={meta.description} />
|
||||
<meta property="og:site_name" content={meta.title} />
|
||||
<meta property="og:description" content={meta.description} />
|
||||
<meta property="og:title" content={meta.title} />
|
||||
<meta property="og:image" content={meta.image} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:site" content="@yourname" />
|
||||
<meta name="twitter:title" content={meta.title} />
|
||||
<meta name="twitter:description" content={meta.description} />
|
||||
<meta name="twitter:image" content={meta.image} />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
46
examples/blog/pages/index.mdx
Normal file
46
examples/blog/pages/index.mdx
Normal file
|
@ -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.
|
||||
|
||||
---
|
||||
|
||||
<div>
|
||||
Twitter [@yourname](https://twitter.com/yourname)
|
||||
<br />
|
||||
GitHub [@yourname](https://github.com/yourname)
|
||||
<br />
|
||||
Instagram [@yourname](https://instagram.com/yourname)
|
||||
<br />
|
||||
Email your@name.com
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
### 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
|
31
examples/blog/pages/photos.mdx
Normal file
31
examples/blog/pages/photos.mdx
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
type: page
|
||||
title: Photos
|
||||
date: 2021-03-18
|
||||
---
|
||||
|
||||
# Photos
|
||||
|
||||
Here's some of my photography.
|
||||
|
||||
import Image from 'next/image'
|
||||
|
||||
<Image
|
||||
src="/images/photo2.jpg"
|
||||
alt="Photo"
|
||||
width={1125}
|
||||
height={750}
|
||||
quality={100}
|
||||
className="next-image"
|
||||
/>
|
||||
[Unsplash ↗ ](https://unsplash.com/photos/WeYamle9fDM)
|
||||
|
||||
<Image
|
||||
src="/images/photo.jpg"
|
||||
alt="Photo"
|
||||
width={1125}
|
||||
height={750}
|
||||
quality={100}
|
||||
className="next-image"
|
||||
/>
|
||||
[Unsplash ↗ ](https://unsplash.com/photos/ndN00KmbJ1c)
|
7
examples/blog/pages/posts/index.md
Normal file
7
examples/blog/pages/posts/index.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
type: posts
|
||||
title: Posts
|
||||
date: 2021-03-18
|
||||
---
|
||||
|
||||
# Posts
|
99
examples/blog/pages/posts/markdown.md
Normal file
99
examples/blog/pages/posts/markdown.md
Normal file
|
@ -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 (
|
||||
<>
|
||||
<Head>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="RSS"
|
||||
href="/feed.xml"
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Inter-roman.latin.var.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
238
examples/blog/pages/posts/pages.md
Normal file
238
examples/blog/pages/posts/pages.md
Normal file
|
@ -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 <div>About</div>
|
||||
}
|
||||
|
||||
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 <div>About</div>
|
||||
}
|
||||
|
||||
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 (
|
||||
<ul>
|
||||
{posts.map((post) => (
|
||||
<li>{post.title}</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
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.
|
13
examples/blog/pages/tags/[tag].mdx
Normal file
13
examples/blog/pages/tags/[tag].mdx
Normal file
|
@ -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 “<TagName/>”
|
BIN
examples/blog/public/favicon.ico
Normal file
BIN
examples/blog/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
examples/blog/public/fonts/Inter-italic.latin.var.woff2
Normal file
BIN
examples/blog/public/fonts/Inter-italic.latin.var.woff2
Normal file
Binary file not shown.
BIN
examples/blog/public/fonts/Inter-roman.latin.var.woff2
Normal file
BIN
examples/blog/public/fonts/Inter-roman.latin.var.woff2
Normal file
Binary file not shown.
BIN
examples/blog/public/images/photo.jpg
Normal file
BIN
examples/blog/public/images/photo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 725 KiB |
BIN
examples/blog/public/images/photo2.jpg
Normal file
BIN
examples/blog/public/images/photo2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 482 KiB |
38
examples/blog/scripts/gen-rss.js
Normal file
38
examples/blog/scripts/gen-rss.js
Normal file
|
@ -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()
|
57
examples/blog/styles/main.css
Normal file
57
examples/blog/styles/main.css
Normal file
|
@ -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;
|
||||
}
|
21
examples/blog/theme.config.js
Normal file
21
examples/blog/theme.config.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
const YEAR = new Date().getFullYear()
|
||||
|
||||
export default {
|
||||
footer: (
|
||||
<small style={{ display: 'block', marginTop: '8rem' }}>
|
||||
<time>{YEAR}</time> © Your Name.
|
||||
<a href="/feed.xml">RSS</a>
|
||||
<style jsx>{`
|
||||
a {
|
||||
float: right;
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
article {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</small>
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue