+ )
+}
diff --git a/dynamic-routes-step-1/components/layout.module.css b/dynamic-routes-step-1/components/layout.module.css
new file mode 100644
index 0000000..d0e3a8f
--- /dev/null
+++ b/dynamic-routes-step-1/components/layout.module.css
@@ -0,0 +1,25 @@
+.container {
+ max-width: 36rem;
+ padding: 0 1rem;
+ margin: 3rem auto 6rem;
+}
+
+.header {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.headerImage {
+ width: 6rem;
+ height: 6rem;
+}
+
+.headerHomeImage {
+ width: 8rem;
+ height: 8rem;
+}
+
+.backToHome {
+ margin: 3rem 0 0;
+}
diff --git a/dynamic-routes-step-1/lib/posts.js b/dynamic-routes-step-1/lib/posts.js
new file mode 100644
index 0000000..edba4e0
--- /dev/null
+++ b/dynamic-routes-step-1/lib/posts.js
@@ -0,0 +1,60 @@
+import fs from 'fs'
+import path from 'path'
+import matter from 'gray-matter'
+
+const postsDirectory = path.join(process.cwd(), 'posts')
+
+export function getSortedPostsData() {
+ // Get file names under /posts
+ const fileNames = fs.readdirSync(postsDirectory)
+ const allPostsData = fileNames.map(fileName => {
+ // Remove ".md" from file name to get id
+ const id = fileName.replace(/\.md$/, '')
+
+ // Read markdown file as string
+ const fullPath = path.join(postsDirectory, fileName)
+ const fileContents = fs.readFileSync(fullPath, 'utf8')
+
+ // Use gray-matter to parse the post metadata section
+ const matterResult = matter(fileContents)
+
+ // Combine the data with the id
+ return {
+ id,
+ ...matterResult.data
+ }
+ })
+ // Sort posts by date
+ return allPostsData.sort((a, b) => {
+ if (a.date < b.date) {
+ return 1
+ } else {
+ return -1
+ }
+ })
+}
+
+export function getAllPostIds() {
+ const fileNames = fs.readdirSync(postsDirectory)
+ return fileNames.map(fileName => {
+ return {
+ params: {
+ id: fileName.replace(/\.md$/, '')
+ }
+ }
+ })
+}
+
+export function getPostData(id) {
+ const fullPath = path.join(postsDirectory, `${id}.md`)
+ const fileContents = fs.readFileSync(fullPath, 'utf8')
+
+ // Use gray-matter to parse the post metadata section
+ const matterResult = matter(fileContents)
+
+ // Combine the data with the id
+ return {
+ id,
+ ...matterResult.data
+ }
+}
diff --git a/dynamic-routes-step-1/package.json b/dynamic-routes-step-1/package.json
new file mode 100644
index 0000000..833fb24
--- /dev/null
+++ b/dynamic-routes-step-1/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "my-app",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start"
+ },
+ "dependencies": {
+ "gray-matter": "^4.0.2",
+ "next": "9.3.5",
+ "react": "16.13.1",
+ "react-dom": "16.13.1"
+ }
+}
diff --git a/dynamic-routes-step-1/pages/_app.js b/dynamic-routes-step-1/pages/_app.js
new file mode 100644
index 0000000..7e66efc
--- /dev/null
+++ b/dynamic-routes-step-1/pages/_app.js
@@ -0,0 +1,5 @@
+import '../styles/global.css'
+
+export default function App({ Component, pageProps }) {
+ return
+}
diff --git a/dynamic-routes-step-1/pages/index.js b/dynamic-routes-step-1/pages/index.js
new file mode 100644
index 0000000..24ba2c6
--- /dev/null
+++ b/dynamic-routes-step-1/pages/index.js
@@ -0,0 +1,40 @@
+import Head from 'next/head'
+import Layout, { siteTitle } from '../components/layout'
+import utilStyles from '../styles/utils.module.css'
+import { getSortedPostsData } from '../lib/posts'
+
+export default function Home({ allPostsData }) {
+ return (
+
+
+ {siteTitle}
+
+
+
[Your Self Introduction]
+
+
+
Blog
+
+ {allPostsData.map(({ id, date, title }) => (
+
+ {title}
+
+ {id}
+
+ {date}
+
+ ))}
+
+
+
+ )
+}
+
+export async function getStaticProps() {
+ const allPostsData = getSortedPostsData()
+ return {
+ props: {
+ allPostsData
+ }
+ }
+}
diff --git a/dynamic-routes-step-1/pages/posts/[id].js b/dynamic-routes-step-1/pages/posts/[id].js
new file mode 100644
index 0000000..89b7f6f
--- /dev/null
+++ b/dynamic-routes-step-1/pages/posts/[id].js
@@ -0,0 +1,31 @@
+import Layout from '../../components/layout'
+import { getAllPostIds, getPostData } from '../../lib/posts'
+
+export default function Post({ postData }) {
+ return (
+
+ {postData.title}
+
+ {postData.id}
+
+ {postData.date}
+
+ )
+}
+
+export async function getStaticPaths() {
+ const paths = getAllPostIds()
+ return {
+ paths,
+ fallback: false
+ }
+}
+
+export async function getStaticProps({ params }) {
+ const postData = getPostData(params.id)
+ return {
+ props: {
+ postData
+ }
+ }
+}
diff --git a/dynamic-routes-step-1/posts/pre-rendering.md b/dynamic-routes-step-1/posts/pre-rendering.md
new file mode 100644
index 0000000..de33130
--- /dev/null
+++ b/dynamic-routes-step-1/posts/pre-rendering.md
@@ -0,0 +1,11 @@
+---
+title: "Two Forms of Pre-rendering"
+date: "2020-01-01"
+---
+
+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** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request.
+- **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**.
+
+Importantly, Next.js lets you **choose** which pre-rendering form 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.
\ No newline at end of file
diff --git a/dynamic-routes-step-1/posts/ssg-ssr.md b/dynamic-routes-step-1/posts/ssg-ssr.md
new file mode 100644
index 0000000..fe4a156
--- /dev/null
+++ b/dynamic-routes-step-1/posts/ssg-ssr.md
@@ -0,0 +1,19 @@
+---
+title: "When to Use Static Generation v.s. Server-side Rendering"
+date: "2020-01-02"
+---
+
+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 that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data.
\ No newline at end of file
diff --git a/dynamic-routes-step-1/public/favicon.ico b/dynamic-routes-step-1/public/favicon.ico
new file mode 100644
index 0000000..4965832
Binary files /dev/null and b/dynamic-routes-step-1/public/favicon.ico differ
diff --git a/dynamic-routes-step-1/public/images/profile.jpg b/dynamic-routes-step-1/public/images/profile.jpg
new file mode 100644
index 0000000..798aba3
Binary files /dev/null and b/dynamic-routes-step-1/public/images/profile.jpg differ
diff --git a/dynamic-routes-step-1/styles/global.css b/dynamic-routes-step-1/styles/global.css
new file mode 100644
index 0000000..9e1b0fb
--- /dev/null
+++ b/dynamic-routes-step-1/styles/global.css
@@ -0,0 +1,27 @@
+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;
+ line-height: 1.6;
+ font-size: 18px;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+a {
+ color: #0070f3;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+img {
+ max-width: 100%;
+ display: block;
+}
diff --git a/dynamic-routes-step-1/styles/utils.module.css b/dynamic-routes-step-1/styles/utils.module.css
new file mode 100644
index 0000000..37f545e
--- /dev/null
+++ b/dynamic-routes-step-1/styles/utils.module.css
@@ -0,0 +1,52 @@
+.heading2Xl {
+ font-size: 2.5rem;
+ line-height: 1.2;
+ font-weight: 800;
+ letter-spacing: -0.05rem;
+ margin: 1rem 0;
+}
+
+.headingXl {
+ font-size: 2rem;
+ line-height: 1.3;
+ font-weight: 800;
+ letter-spacing: -0.05rem;
+ margin: 1rem 0;
+}
+
+.headingLg {
+ font-size: 1.5rem;
+ line-height: 1.4;
+ margin: 1rem 0;
+}
+
+.headingMd {
+ font-size: 1.2rem;
+ line-height: 1.5;
+}
+
+.borderCircle {
+ border-radius: 9999px;
+}
+
+.colorInherit {
+ color: inherit;
+}
+
+.padding1px {
+ padding-top: 1px;
+}
+
+.list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.listItem {
+ margin: 0 0 1.25rem;
+}
+
+.lightText {
+ color: #666;
+}