├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── extensions.json ├── README.md ├── exercises ├── exercise-01--create-a-new-next-js-type-script-project │ └── README.md ├── exercise-02--create-and-navigate-between-pages │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-03--create-dynamic-routes │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-04--serve-static-assets │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── lazar.png │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-05--use-next-image-for-image-optimization │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── lazar.png │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-06--provide-page-specific-metadata │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-07--override-the-app-component │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-08--override-the-document │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-09--create-custom-style │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ ├── src │ │ └── components │ │ │ └── button │ │ │ └── button.tsx │ ├── styles │ │ └── globals.css │ └── tsconfig.json ├── exercise-10--create-custom-layouts │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ ├── src │ │ └── components │ │ │ └── layout │ │ │ ├── footer.tsx │ │ │ ├── index.tsx │ │ │ └── navbar.tsx │ └── tsconfig.json ├── exercise-11--use-ui-frameworks-like-chakra-ui │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ └── index.tsx │ ├── theme.ts │ └── tsconfig.json ├── exercise-12--use-the-static-generation-method │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-13--use-the-server-side-rendering-method │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-14--create-and-use-api-routes │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── data │ │ └── users.ts │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ └── tsconfig.json └── exercise-15--deploy-your-next-js-app-on-vercel │ ├── README.md │ └── readme-assets │ ├── configure-project.png │ ├── import-repository.png │ └── repository-import.png ├── final ├── exercise-01--create-a-new-next-js-type-script-project │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── api │ │ │ └── hello.ts │ │ └── index.tsx │ ├── public │ │ ├── favicon.ico │ │ └── vercel.svg │ ├── styles │ │ ├── Home.module.css │ │ └── globals.css │ └── tsconfig.json ├── exercise-02--create-and-navigate-between-pages │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── about.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-03--create-dynamic-routes │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── [genre] │ │ │ ├── [artist].tsx │ │ │ └── index.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-04--serve-static-assets │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ ├── public │ │ └── lazar.png │ └── tsconfig.json ├── exercise-05--use-next-image-for-image-optimization │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── lazar.png │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-06--provide-page-specific-metadata │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ └── index.tsx │ └── tsconfig.json ├── exercise-07--override-the-app-component │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-08--override-the-document │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _document.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-09--create-custom-style │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ ├── src │ │ └── components │ │ │ └── button │ │ │ ├── button.module.css │ │ │ └── button.tsx │ ├── styles │ │ └── globals.css │ └── tsconfig.json ├── exercise-10--create-custom-layouts │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ └── index.tsx │ ├── src │ │ └── components │ │ │ └── layout │ │ │ ├── footer.tsx │ │ │ ├── index.tsx │ │ │ └── navbar.tsx │ └── tsconfig.json ├── exercise-11--use-ui-frameworks-like-chakra-ui │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ └── index.tsx │ ├── theme.ts │ └── tsconfig.json ├── exercise-12--use-the-static-generation-method │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── [genre].tsx │ │ ├── _app.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-13--use-the-server-side-rendering-method │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── [genre].tsx │ │ ├── _app.tsx │ │ └── index.tsx │ └── tsconfig.json ├── exercise-14--create-and-use-api-routes │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── data │ │ └── users.ts │ ├── next-env.d.ts │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── api │ │ │ └── users │ │ │ │ └── [userId].ts │ │ └── index.tsx │ └── tsconfig.json └── exercise-15--deploy-your-next-js-app-on-vercel │ ├── README.md │ └── readme-assets │ ├── configure-project.png │ ├── import-repository.png │ └── repository-import.png ├── github-share--nextjs-fundamental-api-patterns.png ├── package-lock.json ├── package.json ├── tsconfig.json └── workshop-cover.png /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /**/node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /**/.next/ 14 | /**/out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | **/.DS_Store 21 | *.pem 22 | 23 | # debug 24 | /**npm-debug.log* 25 | /**yarn-debug.log* 26 | /**yarn-error.log* 27 | 28 | # local env files 29 | /**.env.local 30 | /**.env.development.local 31 | /**.env.test.local 32 | /**.env.production.local 33 | 34 | # vercel 35 | .vercel 36 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "none", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Beginner's Guide to Next.js 2 | 3 | [![Workshop cover](./github-share--nextjs-fundamental-api-patterns.png)](https://egghead.io/courses/fundamental-next-js-api-and-patterns-a6a7509f) 4 | 5 | ## 👋 Welcome 6 | 7 | Welcome to The Beginner's Guide to Next.js [egghead.io](https://egghead.io) workshop! 🚀 8 | 9 | In this workshop we will discover the essential features of Next.js. Every exercise is an isolated mini project that focuses only on a specific feature, so you don't have anything extra to worry about. By the end of this workshop, you'll learn how to create Next.js projects, use Next.js's data fetching methods, create your own API handlers, use UI frameworks, and deploy your apps on Vercel. 10 | 11 | I hope you like this workshop, and have fun learning ❤️ 12 | 13 | ## 🤠 Instructor 14 | 15 | My name is Lazar Nikolov and I'm a full stack developer from North Macedonia 🇲🇰, based in Canada 🇨🇦! 16 | 17 | My first interaction with programming was back in 2011, while I was still in high school. I was learning HTML and CSS on my own, sometimes during the classes (don't tell my teachers 🤫). Throughout my career I've been developing mobile apps, TV apps, desktop apps, but I've found my sweet spot as a Full-stack Engineer working with the React ecosystem. 18 | 19 | I'm a super friendly guy, so if you have any questions, or just want to talk to me, the best place to reach me is on Twitter at [@NikolovLazar](https://twitter.com/NikolovLazar). 20 | 21 | ## 🎯 Goal 22 | 23 | After finishing this workshop, you'll become comfortable using Next.js to build your web apps and deploy them on Vercel. 24 | 25 | ## 🚅 Skills 26 | 27 | You'll learn about web page rendering like SSR, SSG, which as a skill is completely transferrable in other frameworks and technologies. 28 | 29 | If you haven't worked with TypeScript before, that's another skill that you'll learn and also apply in other frameworks and technologies. 30 | 31 | We'll also going learn how to architecture our front-end using pages, layouts, and components, which is a skill you can apply in every front-end project in your future. 32 | 33 | ## 🚧 Prerequisites 34 | 35 | Before starting this workshop, you should be comfortable working with React, TypeScript, and understand basic Web Dev concepts. 36 | 37 | Here are some egghead.io courses that can bring you up to speed: 38 | - [The Beginner's Guide to React](https://egghead.io/courses/the-beginner-s-guide-to-react) by Kent C. Dodds 39 | - [React Context for State Management](https://egghead.io/courses/react-context-for-state-management) by Dave Ceddia 40 | - [Shareable Custom Hooks in React](https://egghead.io/courses/shareable-custom-hooks-in-react) by Joe Previte 41 | - [Up and Running with TypeScript](https://egghead.io/courses/up-and-running-with-typescript) by John Lindquist 42 | - [Use Types Effectively in TypeScript](https://egghead.io/courses/use-types-effectively-in-typescript) by Ari Picker 43 | 44 | ## 💽 Workshop Setup 45 | 46 | Make sure you have [Node.js](https://nodejs.org/), [Visual Studio Code](https://code.visualstudio.com/) installed on your machine. 47 | 48 | Each exercise is its own Next.js project and lives inside the `/exercises` folder. To get started, run `npm install` at the root (this installs all of the dependencies), `cd` into `exercises` and open the execise's README file (`exercises/exercise-01--create-a-new-next-js-type-script-project/README.md`), which contains the instructions. Each exercise can be run by running `npm run dev` at the root of the exercise. 49 | 50 | ## 🔢 Exercises list 51 | 52 | - `Exercise 01` - [Create a new Next.js TypeScript project](exercises/exercise-01--create-a-new-next-js-type-script-project) 53 | - `Exercise 02` - [Create and navigate between pages](exercises/exercise-02--create-and-navigate-between-pages) 54 | - `Exercise 03` - [Create Dynamic Routes](exercises/exercise-03--create-dynamic-routes) 55 | - `Exercise 04` - [Serve static assets](exercises/exercise-04--serve-static-assets) 56 | - `Exercise 05` - [Use `next/image` for Image Optimization](exercises/exercise-05--use-next-image-for-image-optimization) 57 | - `Exercise 06` - [Provide page-specific metadata](exercises/exercise-06--provide-page-specific-metadata) 58 | - `Exercise 07` - [Override the App component](exercises/exercise-07--override-the-app-component) 59 | - `Exercise 08` - [Override the Document](exercises/exercise-08--override-the-document) 60 | - `Exercise 09` - [Create custom style](exercises/exercise-09--create-custom-style) 61 | - `Exercise 10` - [Create custom Layouts](exercises/exercise-10--create-custom-layouts) 62 | - `Exercise 11` - [Use UI frameworks like Chakra UI](exercises/exercise-11--use-ui-frameworks-like-chakra-ui) 63 | - `Exercise 12` - [Use the Static Generation method](exercises/exercise-12--use-the-static-generation-method) 64 | - `Exercise 13` - [Use the Server-side Rendering method](exercises/exercise-13--use-the-server-side-rendering-method) 65 | - `Exercise 14` - [Create and use API Routes](exercises/exercise-14--create-and-use-api-routes) 66 | - `Exercise 15` - [Deploy your Next.js app on Vercel](exercises/exercise-15--deploy-your-next-js-app-on-vercel) 67 | 68 | ## ❤️ Contributors 69 | 70 | PRs are always welcome. If you find a typo, or have any ideas on how to improve the content, feel free to submit a PR. Let's make learning Next.js better for everyone! 71 | 72 | ## 🍩 Feedback form 73 | 74 | Your feedback is always welcome 🙏. It's not just useful for me. Your feedback will help me make the content better for every other developer friend in future. Visit [this form](https://forms.gle/fXJdRAT5SBSjGspb7) if you feel like providing a feedback. 75 | 76 | Thank you ❤️ 77 | -------------------------------------------------------------------------------- /exercises/exercise-01--create-a-new-next-js-type-script-project/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 01: Create a new Next.js TypeScript project 2 | 3 | ## What is Next.js? 4 | 5 | Next.js is an open-source React framework built on top of Node.js. It enables developers to build super fast, optimized web sites and web apps. While traditional React apps are rendered on the client-side, Next.js gives you the option to also render your pages on the server-side. Its features like built-in router, per-page rendering options, and automatic static optimization, provide a best in class developer experience and rapid feature development. The React documentation lists Next.js as a "Recommended Toolchain" for building a server-side rendered React apps. It is originally built by Guillermo Rauch, and it is developed and maintained by Vercel. 6 | 7 | ## Background 8 | 9 | NPM has a tool called `npx` used for executing NPM Package Binaries. You can install it by running `npm install -g npx` in your terminal. We can use the `npx` tool to execute packages like `create-react-app` and `create-next-app` that will scaffold our new project. 10 | 11 | ## 🚀 Exercise 12 | 13 | Create an empty Next.js TypeScript project using the [create-next-app](https://www.npmjs.com/package/create-next-app) npx template. Because we want to use TypeScript, we need to provide an additional argument in the CLI command to tell `create-next-app` to create our project with TypeScript. 14 | 15 | ## 🍩 Exercise Feedback form 16 | 17 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+01+-+Create+a+new+Next.js+TypeScript+project) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 02: Create and navigate between pages 2 | 3 | ## Background 4 | 5 | In Next.js, every page is a React component that lives in the `pages/` directory. Every other React component that lives outside of the `pages/` directory is not being considered as a page. Next.js's built-in router generates our routes automatically based on our project structure. For example: 6 | 7 | - The index `/` page would be the `pages/index.tsx` file, 8 | - The `/about` page would be `pages/about.tsx` file, 9 | - The `docs/getting-started` page would be `pages/docs/getting-started.tsx` file, and so on. 10 | 11 | To navigate between pages, Next.js provides us a `Link` component that's exported from the `next/link` package. To create a link to our About page, we can do the following: 12 | 13 | ```typescript 14 | import Link from 'next/link' 15 | 16 | const Home = () => { 17 | return ( 18 | About 19 | ) 20 | } 21 | 22 | export default Home; 23 | ``` 24 | 25 | ## 🚀 Exercise 26 | 27 | Create a new "About" page and add a link in the Home page to it. 28 | 29 | ## 🍩 Exercise Feedback form 30 | 31 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+02+-+Create+and+navigate+between+pages) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-02", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "12.1.5", 13 | "react": "^17", 14 | "react-dom": "^17" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "16.11.10", 18 | "@types/react": "^17", 19 | "eslint": "7.32.0", 20 | "eslint-config-next": "12.0.4", 21 | "typescript": "4.6.3" 22 | } 23 | } -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/pages/index.tsx: -------------------------------------------------------------------------------- 1 | // ✍️ import the Link component from 'next/link' 2 | 3 | const Home = () => { 4 | return ( 5 |
6 |

Welcome to the Home page!

7 | {/* ✍️ put a link to the About page */} 8 |
9 | ) 10 | } 11 | 12 | export default Home 13 | -------------------------------------------------------------------------------- /exercises/exercise-02--create-and-navigate-between-pages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 03: Create Dynamic Routes 2 | 3 | ## Background 4 | 5 | We learned in the previous exercise that Next.js treats our files in the `pages/` directory as pages, and it automatically generates routes for us. But, what if we wanted to create a page with a dynamic route? You can add brackets around the page's name (`[pid].tsx`) to create a dynamic route. 6 | 7 | Let's say we have a page in `pages/post/[pid].tsx`: 8 | 9 | ```typescript 10 | import { useRouter } from 'next/router' 11 | 12 | const Post = () => { 13 | const router = useRouter() 14 | const { pid } = router.query 15 | 16 | return

Post: {pid}

17 | } 18 | 19 | export default Post 20 | ``` 21 | 22 | Any route like `/post/1`, `/post/hello-world`, etc. will be matched by the `[pid].tsx` page. The dynamic parameter `pid` will be available to us as a query parameter through Next.js's router. 23 | 24 | If we want to have a multi-segment dynamic route, we can wrap the folders' names with brackets too, for example `pages/post/[pid]/[comment].tsx`. The router's query will merge the two dynamic segments, so if we visited `/post/hello-world/love-it`, the `query` object will be: 25 | ```typescript 26 | { pid: "hello-world", comment: "love-it" } 27 | ``` 28 | 29 | ## 🚀 Exercise 30 | 31 | Create a genre page that will display the genre's name dynamically. Examples: 32 | - `/rock` 33 | - `/country` 34 | - `/pop` 35 | 36 | ## ⭐️ Challenge 37 | 38 | Make a new dynamic page that will display the artist's name and their music genre. Examples: 39 | - `/rock/axl-rose` should display "axl-rose is a rock music artist!" 40 | - `/country/chris-stapleton` should display "chris-stapleton is a country music artist!" 41 | - `/pop/bruno-mars` should display "bruno-mars is a pop music artist!" 42 | 43 | ## 🚩 Checkpoint 44 | This exercise marks the first checkpoint at which we'll go into breakout rooms to do the following exercises: 45 | - [Exercise 01: Create a new Next.js TypeScript project](../exercise-01--create-a-new-next-js-type-script-project) 46 | - [Exercise 02: Create and navigate between pages](../exercise-02--create-and-navigate-between-pages) 47 | - [Exercise 03: Create Dynamic Routes](../exercise-03--create-dynamic-routes) 48 | 49 | Start with the first exercise, and work your way through the last one. If you're stuck, do not hesitate to ask. As soon as you're done you can fill out either the [Post-Breakout Feedback Form](https://docs.google.com/forms/d/e/1FAIpQLSda0BFV57OWbSkshNC_jZ809HEBOuW_MzLEz1Bq4PVKtI7R9w/viewform?usp=pp_url&entry.651170566=Group+1:+New+Project,+Pages+%26+Navigation,+Dynamic+Routes), or the feedback forms for each individual exercise. I would appreciate your feedback a lot. 50 | 51 | ## 🍩 Exercise Feedback form 52 | 53 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+03+-+Create+Dynamic+Routes) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-03", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "12.1.5", 13 | "react": "^17", 14 | "react-dom": "^17" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "16.11.10", 18 | "@types/react": "^17", 19 | "eslint": "7.32.0", 20 | "eslint-config-next": "12.0.4", 21 | "typescript": "4.6.3" 22 | } 23 | } -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | 3 | const Home = () => { 4 | return ( 5 |
6 |

My favorite music genres:

7 |
    8 |
  • 9 | Rock 10 |
  • 11 |
  • 12 | Country 13 |
  • 14 |
  • 15 | Pop 16 |
  • 17 |
18 |
19 | ) 20 | } 21 | 22 | export default Home 23 | -------------------------------------------------------------------------------- /exercises/exercise-03--create-dynamic-routes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 04: Serve static assets 2 | 3 | ## Background 4 | 5 | Aside from `pages`, there is another "specialized" folder called `public`. Next.js serves everything in the `public` folder statically, starting from the base URL `/`. If we had an image `public/lazar.png` we can view it on `http://localhost:3000/lazar.png`, or we can reference it in our code: 6 | 7 | ```typescript 8 | const Avatar = () => { 9 | return Lazar Nikolov 10 | } 11 | 12 | export default Avatar 13 | ``` 14 | 15 | The `public` folder is also useful for the `robots.txt`, `favicon.ico`, Google Site Verification, and any other static files, including HTML files. 16 | 17 | ## 🚀 Exercise 18 | 19 | Add the `"lazar.png"` image (which is at the root of this project) to the Home page. 20 | 21 | ## 🍩 Exercise Feedback form 22 | 23 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+04+-+Serve+static+assets) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/lazar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikolovlazar/egghead-beginners-guide-nextjs/39d79b47c699e9c4c606da2942ffa5b1c3b3816a/exercises/exercise-04--serve-static-assets/lazar.png -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-04", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "12.1.5", 13 | "react": "^17", 14 | "react-dom": "^17" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "16.11.10", 18 | "@types/react": "^17", 19 | "eslint": "7.32.0", 20 | "eslint-config-next": "12.0.4", 21 | "typescript": "4.6.3" 22 | } 23 | } -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/pages/index.tsx: -------------------------------------------------------------------------------- 1 | const Home = () => { 2 | return ( 3 |
4 |

Hello 👋

5 |

My name is Lazar Nikolov

6 | {/* ✍️ add the "lazar.png" image here */} 7 |
8 | ) 9 | } 10 | 11 | export default Home 12 | -------------------------------------------------------------------------------- /exercises/exercise-04--serve-static-assets/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 05: Use `next/image` for Image Optimization 2 | 3 | ## Background 4 | 5 | We can always use the good old `` HTML element to display images, but Next.js also provides an `Image` component that has some built-in performance optimizations: 6 | - Improved Performance: Always serves correctly sized image for each device, using modern image formats. 7 | - Visual Stability: Prevents Cumulative Layout Shift automatically. 8 | - Faster Page Loads: Images are only loaded when they enter the viewport, with optional blur-up placeholders. 9 | - Asset Flexibility: On-demand image resizing, even for images stored on remote servers. 10 | 11 | Here's how we can use the `Image` component: 12 | 13 | ```typescript 14 | // pages/index.tsx 15 | 16 | import Image from 'next/image' 17 | 18 | import photo from '../public/photo.png' 19 | 20 | const Home = () => { 21 | return ( 22 | Picture of Lazar 23 | ) 24 | } 25 | 26 | export default Home 27 | ``` 28 | 29 | When we use static imports like in the example above, Next.js's `Image` component automatically sets the `width`, `height`, `placeholder` and `blurDataURL` props because it can analyze it on build-time. 30 | 31 | If we want to load a remote image, it's up to us to provide the `width` and `height` props. Because we always want our apps to be secure, Next.js also requires us to provide the domains from which we'll load remote images from: 32 | 33 | ```typescript 34 | // next.config.js 35 | 36 | module.exports = { 37 | images: { 38 | domains: ['example.com', 'cdnurl.com', ...] 39 | } 40 | } 41 | ``` 42 | 43 | To improve our website performance, we can add a `priority` prop to the image that would be the [Largest Contentful Paint (LCP)](https://web.dev/lcp/#what-elements-are-considered) element for each page. This will tell Next.js to prioritize the loading of that image, and that will make our LCP score much better. The LCP element is usually the largest image visible in the viewport of the page. When you run next dev, you'll see a console warning if the LCP element is an without the priority property. Once you've identified which element is the LCP, you can add the `priority` prop like this: 44 | 45 | ```typescript 46 | // pages/index.tsx 47 | 48 | import Image from 'next/image' 49 | 50 | import photo from '../public/photo.png' 51 | 52 | const Home = () => { 53 | return ( 54 | Picture of Lazar 59 | ) 60 | } 61 | 62 | export default Home 63 | ``` 64 | 65 | ## 🚀 Exercise 66 | 67 | Add the `"lazar.png"` image (which is at the root of this project) to the Home page using the `Image` component. 68 | 69 | ## ⭐️ Challenge 70 | 71 | Display a remote image from Twitter using the `Image` component. 72 | 73 | ## 🍩 Exercise Feedback form 74 | 75 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+05+-+Use+next/image+for+Image+Optimization) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/lazar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nikolovlazar/egghead-beginners-guide-nextjs/39d79b47c699e9c4c606da2942ffa5b1c3b3816a/exercises/exercise-05--use-next-image-for-image-optimization/lazar.png -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-05", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/react": "^1.8.8", 13 | "@emotion/react": "^11", 14 | "@emotion/styled": "^11", 15 | "framer-motion": "^6", 16 | "next": "12.1.5", 17 | "react": "^17", 18 | "react-dom": "^17" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "16.11.10", 22 | "@types/react": "^17", 23 | "eslint": "7.32.0", 24 | "eslint-config-next": "12.0.4", 25 | "typescript": "4.6.3" 26 | } 27 | } -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/pages/index.tsx: -------------------------------------------------------------------------------- 1 | // ✍️ import the Image component from 'next/image' 2 | 3 | // ✍️ import "lazar.png" statically 4 | 5 | const Home = () => { 6 | return { 7 | /* ✍️ add the lazar image here */ 8 | } 9 | } 10 | 11 | export default Home 12 | -------------------------------------------------------------------------------- /exercises/exercise-05--use-next-image-for-image-optimization/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 06: Provide page-specific metadata 2 | 3 | ## Background 4 | Something that every website has in common is a metadata, which consists of a title, description, style links, scripts and so on. In Next.js, we can use the `Head` component to provide metadata for each page specifically: 5 | 6 | ```typescript 7 | // pages/index.tsx 8 | 9 | import Head from 'next/head' 10 | 11 | const Home = () => { 12 | return ( 13 | <> 14 | 15 | Homepage 16 | 17 | 18 |
19 |

Homepage

20 |
21 | 22 | ) 23 | } 24 | 25 | export default Home 26 | ``` 27 | 28 | The content in the `Head` component can be composed of local hardcoded data, and also obtained from an API or CMS. In order to avoid duplicates, we can use the `key` prop, which will make sure that the tag is going to be rendered only once: 29 | 30 | ```typescript 31 | // pages/index.tsx 32 | 33 | import Head from 'next/head' 34 | 35 | const Home = () => { 36 | return ( 37 | <> 38 | 39 | Homepage 40 | 41 | 42 | 43 | 44 | // <--- this tag will be rendered 45 | 46 |
47 |

Homepage

48 |
49 | 50 | ) 51 | } 52 | 53 | export default Home 54 | ``` 55 | 56 | In this case, only the second ("My Next.js course") tag is going to be rendered. 57 | 58 | > The contents of the `Head` component get cleared upon unmounting the component, so make sure that each page completely defines what it needs without making assumptions about what other pages added. 59 | 60 | The `title`, `meta`, or any other elements need to be direct children of the `Head` component, or wrapped into maximum one level of ``, otherwise the tags won't be correctly picked up on client-side navigations. 61 | 62 | ## 🚀 Exercise 63 | 64 | Add a title and description using the `Head` component in our homepage. 65 | 66 | ## 🚩 Checkpoint 67 | This exercise marks the second checkpoint at which we'll go into breakout rooms to do the following exercises: 68 | - [Exercise 04: Serve static assets](../exercise-04--serve-static-assets) 69 | - [Exercise 05: Use next/image for image optimization](../exercise-05--use-next-image-for-image-optimization) 70 | - [Exercise 06: Provide page-specific metadata](../exercise-06--provide-page-specific-metadata) 71 | 72 | Start with the first exercise, and work your way through the last one. If you're stuck, do not hesitate to ask. As soon as you're done you can fill out either the [Post-Breakout Feedback Form](https://docs.google.com/forms/d/e/1FAIpQLSda0BFV57OWbSkshNC_jZ809HEBOuW_MzLEz1Bq4PVKtI7R9w/viewform?usp=pp_url&entry.651170566=Group+2:+Static+Assets,+Next+Image,+Page+Metadata), or the feedback forms for each individual exercise. I would appreciate your feedback a lot. 73 | 74 | ## 🍩 Exercise Feedback form 75 | 76 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+06+-+Provide+page-specific+metadata) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-06", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/react": "^1.8.8", 13 | "@emotion/react": "^11", 14 | "@emotion/styled": "^11", 15 | "framer-motion": "^6", 16 | "next": "12.1.5", 17 | "react": "^17", 18 | "react-dom": "^17" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "16.11.10", 22 | "@types/react": "^17", 23 | "@types/react-dom": "^17", 24 | "eslint": "7.32.0", 25 | "eslint-config-next": "12.0.4", 26 | "typescript": "4.6.3" 27 | } 28 | } -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/pages/index.tsx: -------------------------------------------------------------------------------- 1 | // ✍️ import Head from 'next/head' 2 | import { ChakraProvider, Heading } from '@chakra-ui/react' 3 | 4 | const Home = () => { 5 | return ( 6 | 7 | {/* ✍️ add title and description using the Head component */} 8 | 9 | This is our homepage content! 10 | 11 | 12 | ) 13 | } 14 | export default Home 15 | -------------------------------------------------------------------------------- /exercises/exercise-06--provide-page-specific-metadata/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 07: Override the App Component 2 | 3 | ## Background 4 | Every page in Next.js is initialized by an `App` component. By default it's hidden (internal in Next.js), but we can override it to control the page initialization if we want to: 5 | - Persist layout between page changes 6 | - Keep state when navigating pages 7 | - Custom error handling 8 | - Inject additional data into pages 9 | - Add global CSS 10 | 11 | To override the default `App`, we need to create the `pages/_app.tsx` file: 12 | 13 | ```typescript 14 | import type { AppProps } from 'next/app' 15 | 16 | const App = ({ Component, pageProps }: AppProps) => { 17 | return 18 | } 19 | 20 | export default App 21 | ``` 22 | 23 | > Mind the underscore before "app": `_app.tsx` 24 | 25 | The `Component` component is our currently displayed page. The `pageProps` object contains the initial props that were preloaded by the data fetching methods. If no data fetching methods were used in that particular page, the `pageProps` object will be empty. 26 | 27 | ## 🚀 Exercise 28 | 29 | Override the `App` component and pass a `title` prop to the `Component`. 30 | 31 | ## 🍩 Exercise Feedback form 32 | 33 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+07+-+Override+the+App+component) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-07", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "12.1.5", 13 | "react": "^17", 14 | "react-dom": "^17" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "16.11.10", 18 | "@types/react": "^17", 19 | "eslint": "7.32.0", 20 | "eslint-config-next": "12.0.4", 21 | "typescript": "4.6.3" 22 | } 23 | } -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/pages/index.tsx: -------------------------------------------------------------------------------- 1 | // ✍️ create a type Props containing title as string 2 | 3 | // ✍️ obtain the title prop 4 | const Home = () => { 5 | return ( 6 |
7 |

Overriding the App component

8 | {/* ✍️ render the title prop */} 9 |
10 | ) 11 | } 12 | 13 | export default Home 14 | -------------------------------------------------------------------------------- /exercises/exercise-07--override-the-app-component/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 08: Override the Document 2 | 3 | ## Background 4 | Just like `_app.tsx`, we can also override the `_document.tsx` file. We can use a custom `Document` to augment our application's `` and `` tags, like setting the `lang` property of the `` tag, adding third-party scripts, linking custom fonts, add analytics scripts etc... 5 | 6 | To override the default `Document`, we need to create a file `pages/_document.tsx`: 7 | 8 | ```typescript 9 | import { Html, Head, Main, NextScript } from 'next/document' 10 | 11 | export default function Document() { 12 | return ( 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | ) 21 | } 22 | ``` 23 | > 💡 The ``, ``, `
` and `` are required for the page to be properly rendered! 24 | 25 | Note that the `Document` is only rendered in the server, so event handlers like `onClick` will not work! 26 | 27 | ## 🚀 Exercise 28 | Override the `Document`. 29 | 30 | ## 🍩 Exercise Feedback form 31 | 32 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+08+-+Override+the+Document) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-08", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/react": "^1.8.8", 13 | "@emotion/react": "^11", 14 | "@emotion/styled": "^11", 15 | "framer-motion": "^6", 16 | "next": "12.1.5", 17 | "react": "^17", 18 | "react-dom": "^17" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "16.11.10", 22 | "@types/react": "^17", 23 | "eslint": "7.32.0", 24 | "eslint-config-next": "12.0.4", 25 | "typescript": "4.6.3" 26 | } 27 | } -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/pages/index.tsx: -------------------------------------------------------------------------------- 1 | const Home = () => { 2 | return ( 3 |
4 |

We are overriding the Document in Next.js

5 |

Nothing to see here

6 |
7 | ) 8 | } 9 | 10 | export default Home 11 | -------------------------------------------------------------------------------- /exercises/exercise-08--override-the-document/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 09: Create custom style 2 | 3 | ## Background 4 | 5 | In Next.js, there are several ways to achieve custom styles: 6 | - [Adding a Global Stylesheet](#adding-a-global-stylesheet) 7 | - [Component-level CSS](#component-level-css) 8 | - [CSS-in-JS](#css-in-js) 9 | 10 | ### Adding a Global Stylesheet 11 | To add a global stylesheet, we need to import it in the `pages/_app.tsx` file. If we haven't overriden the `App` yet, we should do that first. 12 | 13 | Let's say we have the following file `styles.css` at the root of the project: 14 | 15 | ```css 16 | body { 17 | font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', 18 | 'Arial', sans-serif; 19 | padding: 20px 20px 60px; 20 | max-width: 680px; 21 | margin: 0 auto; 22 | } 23 | ``` 24 | 25 | To use it, we need to simply import it in our `_app.tsx` file: 26 | 27 | ```typescript 28 | import type { AppProps } from 'next/app' 29 | 30 | import '../styles.css' 31 | 32 | const App = ({ Component, pageProps }: AppProps) => { 33 | return 34 | } 35 | 36 | export default App 37 | ``` 38 | 39 | Since we're importing them in our `_app.tsx` file, these styles will be applied to every page. 40 | 41 | > Since Next.js 9.5.4, we can also import styles from `node_modules`, for example: `import 'bootstrap/dist/css/bootstrap.css'` will import Bootstrap and it will work just fine. 42 | 43 | ### Component-level CSS 44 | 45 | Another way of achieving custom styles is using [CSS Modules](https://github.com/css-modules/css-modules), which Next.js has support for. 46 | 47 | Let's say we want to make a reusable Button component. First, we're going to create the CSS module `src/components/button/button.module.css`: 48 | 49 | ```css 50 | .error { 51 | color: white; 52 | background-color: red; 53 | } 54 | ``` 55 | Then, we're going to create the component itself `src/components/button/button.tsx`: 56 | 57 | ```typescript 58 | import styles from './button.module.css' 59 | 60 | const Button = () => { 61 | return ( 62 | 68 | ) 69 | } 70 | 71 | export default Button 72 | ``` 73 | 74 | ### CSS-in-JS 75 | 76 | Next.js also comes with a built-in CSS-in-JS support. The simplest way to do CSS-in-JS is inline styles: 77 | 78 | ```typescript 79 | const Button = () => { 80 | return ( 81 | 87 | ) 88 | } 89 | 90 | export default Button 91 | ``` 92 | 93 | ## 🚀 Exercise 94 | Try all three methods: 95 | - Create a global stylesheet that defines the container layout 96 | - Create a flexbox `
` using CSS-in-JS 97 | - Create a "Log in" `Button` component that uses CSS Modules and put it in the flexbox 98 | 99 | ## 🍩 Exercise Feedback form 100 | 101 | Writing down what you learn is key to your retention. Also, I want to make sure each exercise is effective at helping you learn the material. Please quickly fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSeKPJV5UInaNFlZawN7vZdNyPngyinrkp7eoQO0vzwGzh2EtQ/viewform?usp=pp_url&entry.651170566=Exercise+09+-+Create+custom+style) so you can elaborate on what you learned and give me feedback so I can improve it for future learners. -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercise-09", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "next": "12.1.5", 13 | "react": "^17", 14 | "react-dom": "^17" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "16.11.10", 18 | "@types/react": "^17", 19 | "eslint": "7.32.0", 20 | "eslint-config-next": "12.0.4", 21 | "typescript": "4.6.3" 22 | } 23 | } -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from 'next/app' 2 | 3 | // ✍️ import the global stylesheet 4 | 5 | const App = ({ Component, pageProps }: AppProps) => { 6 | return 7 | } 8 | 9 | export default App 10 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Button from '../src/components/button/button' 2 | 3 | const Home = () => { 4 | return ( 5 |
13 |

My App

14 |
16 | ) 17 | } 18 | 19 | export default Home 20 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/src/components/button/button.tsx: -------------------------------------------------------------------------------- 1 | // ✍️ import the button's CSS module 2 | 3 | const Button = () => { 4 | // ✍️ set the button's className to the class from the CSS module 5 | return 6 | } 7 | 8 | export default Button 9 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/styles/globals.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', 3 | 'Arial', sans-serif; 4 | padding: 20px 20px 60px; 5 | max-width: 680px; 6 | margin: 0 auto; 7 | } 8 | -------------------------------------------------------------------------------- /exercises/exercise-09--create-custom-style/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-10--create-custom-layouts/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /exercises/exercise-10--create-custom-layouts/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /exercises/exercise-10--create-custom-layouts/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 10: Create custom Layouts 2 | 3 | ## Background 4 | 5 | React's composable nature allows us to create reusable components. Layouts are exactly that! In [Exercise 07](../exercise-07--override-the-app-component) we learned what's the `App` component and how to override it. So, if we had a custom layout and wanted to use it, the `App` component is where we should start. 6 | 7 | In Next.js there are two ways that you can define a custom layout: 8 | - [Single Shared Layout](#single-shared-layout) 9 | - [Per-Page Layouts](#per-page-layouts) 10 | 11 | ## Single Shared Layout 12 | 13 | A Single Shared Layout in Next.js is a custom layout that's used by every page in our app. Let's say our app is simple, and every page has a `navbar` and a `footer`. We can define our layout like so: 14 | 15 | ```typescript 16 | // src/components/layout/index.tsx 17 | 18 | import type { ReactNode } from 'react' 19 | 20 | import Navbar from './navbar' 21 | import Footer from './footer' 22 | 23 | type Props = { 24 | children?: ReactNode 25 | } 26 | 27 | const Layout = ({ children }: Props) => { 28 | return ( 29 | <> 30 | 31 |
{children}
32 |