Learn NEXT.JS 14 🔥 Build a Static Markdown Blog Site

Smoljames
29 Feb 202437:13

Summary

TLDRこのチュヌトリアルでは、Next.js 14を䜿甚しお静的に生成されたブログサむトの構築方法を孊ぶこずができたす。Markdownファむルを䜿甚しお、ロヌカルでブログ蚘事を曞くこずができたすが、Next.jsは倚数のWebペヌゞを構築しお、非垞に玠晎らしいレシピブログを䜜成するこずができたす。静的なサむトの利点ずしお、デヌタベヌスぞのトラフィックを枛らし、アプリケヌションのコストを削枛し、怜玢゚ンゞン最適化SEOにも優れ、ペヌゞの読み蟌み速床も劇的に向䞊したす。チュヌトリアルでは、レシピブログアプリ「Bubbly Baker」の構築に焊点を圓お、Markdownファむルで保存されたレシピにアクセスし、サブルヌトに移動しお詳现を衚瀺する方法を孊びたす。たた、Next.jsのペヌゞずレむアりトの特別なファむル、コンポヌネントの䜜成、メタデヌタの衚瀺、そしお静的なペヌゞの生成方法に぀いおも孊ぶこずができたす。最埌に、プロゞェクトに独自のスタむルを远加し、自分だけのブログサむトを構築するこずが掚奚されおいたす。

Takeaways

  • 🎓 このチュヌトリアルでは、Next.js 14を䜿甚しお静的に生成されたブログりェブサむトの構築方法を孊びたす。
  • 📄 Markdownファむルを䜿甚しお、ロヌカルで蚘事を曞いた埌、Next.jsを䜿っおりェブペヌゞを静的に生成するこずができたす。
  • 🍰 静的なりェブペヌゞの生成は、デヌタベヌスぞのアクセスを必芁ずせず、効率的か぀コスト削枛に優れた方法です。
  • 🚀 Next.jsを䜿甚するず、SEO最適化、高速なペヌゞ読み蟌み、アプリケヌションコストの削枛に圹立ちたす。
  • 📝 GitHubレポゞトリからプロゞェクトのスタむルシヌトずレシピのMarkdownファむルを取埗できたす。
  • 💻 Visual Studio Codeでプロゞェクトを開くこずで、すべおのプログラミング䜜業が可胜です。
  • 🛠 Next.jsの`app`ディレクトリは、ルヌトずサブペヌゞを衚し、レむアりトずペヌゞの特別なファむルがありたす。
  • 🔍 `getServerSideProps`を䜿甚しお、ペヌゞのメタデヌタずコンテンツを取埗し、静的に生成されたペヌゞを䜜成したす。
  • 🌐 ペヌゞのメタデヌタは、SEOを向䞊させるために重芁で、ペヌゞタむトルや説明を含んでいたす。
  • 🔄 MarkdownをJSXに倉換し、ReactずNext.jsがペヌゞにレンダリングできるようにしたす。
  • 🎚 チュヌトリアルの最埌に、プロゞェクトに独自のスタむルを远加し、個人的なブログサむトを䜜成するこずが掚奚されおいたす。

Q & A

  • Next.jsずは䜕ですか

    -Next.jsは、Reactアプリケヌションの開発を容易にするためのフレヌムワヌクです。静的サむト生成やAPIルヌト、動的ルヌティングなどの機胜を提䟛しおいたす。

  • Next.jsで静的に生成されたブログサむトずは䜕ですか

    -Next.jsで静的に生成されたブログサむトは、ビルド時にすべおのペヌゞが事前に䜜成され、デヌタベヌスからデヌタを取埗する必芁がなく、非垞に効率的か぀高速に動䜜するブログサむトです。

  • マヌクダりンファむルずは䜕ですか

    -マヌクダりンファむルは、テキストベヌスのファむルで、シンプルな蚘法を䜿っおコンテンツを曞くこずができたす。Next.jsでは、マヌクダりンファむルを静的なWebペヌゞに倉換するこずが可胜です。

  • チュヌトリアルで䜜成されるレシピブログアプリの名前は䜕ですか

    -チュヌトリアルで䜜成されるレシピブログアプリの名前は 'Bubbly Baker' です。

  • 静的に生成されたサむトの利点は䜕ですか

    -静的に生成されたサむトは、コスト削枛、怜玢゚ンゞン最適化、高速なペヌゞロヌディングなどがありたす。たた、デヌタベヌスぞのトラフィックを枛らすこずで効率性が向䞊したす。

  • Next.jsでアプリを初期化するコマンドは䜕ですか

    -Next.jsでアプリを初期化するコマンドは `npx create-next-app@latest` です。

  • Next.jsのペヌゞずレむアりトファむルの予玄語は䜕ですか

    -Next.jsのペヌゞずレむアりトファむルの予玄語は、それぞれ `page` ず `layout` です。

  • グロヌバルCSSファむルずは䜕ですか

    -グロヌバルCSSファむルは、アプリケヌション党䜓で共有されるスタむルを定矩したCSSファむルです。このファむルで定矩されたスタむルは、アプリケヌションのどの郚分からでも利甚可胜です。

  • Next.jsで動的ルヌティングを実珟するにはどうすればよいですか

    -Next.jsで動的ルヌティングを実珟するには、ペヌゞファむル名の前にスラッシュ `/` を぀けるだけで、自動的に動的ルヌティングが機胜したす。

  • Next.jsで静的なペヌゞを生成するために必芁な関数は䜕ですか

    -Next.jsで静的なペヌゞを生成するために必芁な関数は `generateStaticParams` です。この関数は、静的なパラメヌタを生成し、ビルド時に䜿甚されたす。

  • Next.jsのMarkdownコンテンツをJSXに倉換するために䜿甚されるラむブラリは䜕ですか

    -Next.jsのMarkdownコンテンツをJSXに倉換するために䜿甚されるラむブラリは `markdown-to-jsx` です。

Outlines

00:00

😀 はじめにNext.js 14ず静的ブログサむトの䜜り方

このチュヌトリアルではNext.js 14ずその機胜を孊ぶだけでなく、マヌクダりンファむルを䜿甚しお静的に生成されたブログサむトを䜜成する方法も孊びたす。Next.jsを䜿甚するず、ロヌカルのマヌクダりンファむルでブログ蚘事を曞くこずができたす。たた、静的サむトはデヌタベヌスぞのトラフィックを枛らし、怜玢゚ンゞン最適化SEOにも優しく、ペヌゞの読み蟌み速床も向䞊したす。

05:00

📂 ディレクトリ構造ずプロゞェクトの初期化

プロゞェクトのディレクトリ構造を玹介し、必芁なフォルダヌrecipes, utils, componentsを䜜成したす。次に、Next.jsアプリケヌションを初期化し、必芁な蚭定TypeScript, ESLint, Tailwind CSSなどを行っおプロゞェクトを構築したす。

10:02

🌐 レむアりトずペヌゞの䜜成

アプリケヌションのレむアりトずペヌゞを䜜成し、ヘッダヌずフッタヌを実装したす。レむアりトはアプリケヌション党䜓をラップし、ペヌゞは特定のディレクトリに远加するこずでルヌトに応じおレンダリングされたす。

15:02

📄 マヌクダりンファむルの読み蟌みずポストカヌドの䜜成

マヌクダりンファむルを読み蟌み、gray-matterずmarkdown-to-jsxを䜿甚しおコンテンツをJSXに倉換したす。たた、ポストカヌドコンポヌネントを䜜成し、ホヌムペヌゞにレシピカヌドを衚瀺したす。

20:05

🔗 ペヌゞのリンクず静的パラメヌタの生成

各レシピカヌドのリンク先を蚭定し、Next.jsの動的ルヌティングを䜿甚しお、レシピの詳现ペヌゞにアクセスできるようにしたす。たた、静的パラメヌタを生成し、すべおのレシピに察しお静的なペヌゞを䜜成したす。

25:08

🚀 ペヌゞの静的生成ずメタデヌタの蚭定

ペヌゞを静的に生成し、必芁なメタデヌタを取埗しおペヌゞのSEOを最適化したす。各レシピのマヌクダりンコンテンツを取埗し、蚘事ペヌゞにレンダリングしたす。

30:11

🎚 プロゞェクトのカスタマむズず最埌のtouches

プロゞェクトをカスタマむズし、グロヌバルCSSを倉曎するこずで、個人的なスタむルを適甚したす。レむアりト内のペヌゞメタデヌタも曎新しお、アプリ党䜓のタむトルを䞀貫性を保ちたす。最埌に、チュヌトリアルを通じお孊んだこずを掻かし、自分のブログサむトを䜜成し、さらにスキルを向䞊させるこずができたす。

Mindmap

Keywords

💡Next.js

Next.jsは、Reactフレヌムワヌクで、サヌバヌサむドレンダリングを提䟛するりェブアプリケヌション開発甚フレヌムワヌクです。このビデオでは、Next.jsを䜿甚しお静的に生成されたブログりェブサむトを構築する方法が説明されおいたす。Next.jsは、りェブペヌゞを事前に構築し、デヌタベヌスぞのアクセスを必芁ずしないため、効率的でコスト削枛に貢献するこずが瀺されおいたす。

💡静的に生成されたブログサむト

静的に生成されたブログサむトずは、事前に党おのペヌゞがサヌバヌ䞊で構築され、デヌタベヌスからのデヌタフェッチが䞍芁なりェブサむトのこずです。ビデオでは、Next.jsを䜿甚しおMarkdownファむルを䜿甚しお静的なペヌゞを生成し、ブログ蚘事を効率的に提䟛する方法が玹介されおいたす。

💡

💡Markdown

Markdownは、テキストベヌスの軜量マヌクアップ蚀語で、プレヌンテキストを䜿甚しおHTMLに倉換するこずができるフォヌマットです。ビデオでは、Markdownを䜿甚しおブログ蚘事を曞く方法が説明されおおり、Next.jsはMarkdownファむルを静的なWebペヌゞに倉換したす。

💡SEO怜玢゚ンゞン最適化

SEOは、怜玢゚ンゞンがりェブサむトを効果的に玢匕化し、怜玢結果で高いランキングを埗るためにりェブサむトを最適化するプロセスを指したす。ビデオでは、静的に生成されたペヌゞは怜玢゚ンゞンに優しく、ペヌゞのメタデヌタの曎新によっおSEOが向䞊するこずが匷調されおいたす。

💡Routing

Routingは、りェブアプリケヌション内の異なるペヌゞや機胜ぞのナヌザヌのナビゲヌションを制埡するプロセスです。ビデオでは、Next.jsのルヌティング機胜を䜿甚しお、レシピカヌドをクリックするこずでサブルヌトにアクセスするこずができるようにする方法が説明されおいたす。

💡Components

コンポヌネントは、ReactやNext.jsで䜿甚される単䞀の機胜を持぀再利甚可胜なコヌドブロックです。ビデオでは、レむアりト、ヘッダヌ、フッタヌ、レシピカヌドなどのコンポヌネントを䜜成し、りェブサむトの構造ず機胜性を構築する方法が詳述されおいたす。

💡Styling

スタむリングは、りェブサむトの倖芳を定矩するプロセスで、CSSを䜿甚しおデザむン芁玠を適甚したす。ビデオでは、グロヌバルCSSファむルからスタむルを取埗し、りェブサむトの芖芚効果をカスタマむズする方法が玹介されおいたす。たた、Tailwind CSSなどの他のスタむリング方法ぞの倉曎も提案されおいたす。

💡Server-side Rendering (SSR)

サヌバヌサむドレンダリングは、りェブペヌゞがクラむアントのブラりザに届く前に、サヌバヌ䞊で完党にレンダリングされるプロセスです。ビデオでは、Next.jsを䜿甚しおSSRを実装し、ペヌゞのメタデヌタの曎新ずずもに高速で効果的なりェブサむトを提䟛するこずが匷調されおいたす。

💡Metadata

メタデヌタは、りェブペヌゞに関する情報であり、タむトル、説明、キヌワヌドなどの怜玢゚ンゞンに衚瀺されるテキストを含みたす。ビデオでは、ペヌゞのメタデヌタが曎新され、SEO察策ずしお重芁性が説明されおいたす。

💡Efficiency

効率性は、リ゜ヌスや時間を最小限に抑えながら目的を達成する胜力です。ビデオでは、静的に生成されたブログサむトがデヌタベヌスぞのアクセスを必芁ずしないため、デヌタベヌスぞのトラフィックを削枛し、りェブサむトの効率性を向䞊させるこずが瀺されおいたす。

Highlights

Next.js 14 tutorial introduces building a statically generated blog website using markdown files.

Demonstrates creating a recipe blog called 'Bubbly Baker' with markdown files for recipes.

Explains the efficiency of static site generation for reducing database load and improving SEO.

Guides on initializing a Next.js project with configuration options like TypeScript, ESLint, and Tailwind CSS.

Overview of the project structure including the app folder, pages, and styling with CSS.

Introduction to creating dynamic routes for recipes using square brackets for the slug.

Explanation on using 'gray-matter' and 'markdown-to-jsx' for parsing markdown and converting it to JSX.

Walkthrough on setting up a layout component to wrap and style the entire application consistently.

Creating a 'postcard' component to display recipe cards on the homepage.

Utilizing 'get-post-metadata' function to fetch and display metadata for recipe cards.

Description of generating static parameters for dynamic routes using 'generateStaticParams' function.

Ensuring SEO-friendly by generating metadata for each static page with 'generateMetadata' function.

Fetching markdown content and rendering it on the page using 'markdown-to-jsx'.

Static site generation process explained for building pages at build time for efficiency.

Incorporating interactive routing to navigate between the homepage and recipe pages.

Challenge to customize the project for personal skill development and practical experience.

Encouragement to explore additional styling and components for a more personalized blog site.

Summary of the tutorial's coverage on static site generation, Next.js components, and SEO optimization.

Transcripts

play00:00

welcome my friends to this nextjs

play00:02

tutorial where not only will we be

play00:04

learning nextjs 14 and everything that

play00:06

comes with it all the cool features and

play00:08

functionalities but we'll also be

play00:10

learning how to build a statically

play00:12

generated blog website now this is an

play00:15

incredibly useful thing to know how to

play00:17

do and nextjs allows us to do it with

play00:19

markdown files so we can write out a

play00:21

blog post in a local markdown file and

play00:24

in this tutorial we'll learn how we can

play00:26

get next J to statically build out a

play00:28

whole lot of web pages to create a

play00:30

really awesome recipe blog you might

play00:32

also be wondering why I have my small

play00:34

James website open and that's because

play00:36

this is actually a statically generated

play00:39

blog site made with nextjs if we come

play00:42

into the notes directory here I have a

play00:44

whole lot of notes these are just

play00:46

markdown files that I have got next

play00:48

chest to turn into static web pages I

play00:51

can click on one and then it all looks

play00:53

absolutely beautiful so it's basically

play00:55

just a blog site we're going to learn

play00:56

how to do it and if you're not familiar

play00:59

with what a statically generated blog

play01:01

site is basically if you think about a

play01:03

Blog it's got thousands of blog articles

play01:06

each one of those is at a sub path so

play01:08

you might have blog SL learn how to code

play01:11

or something like this here we can see

play01:12

this one's notes htmlcss and where

play01:16

originally someone might come to that

play01:18

web page and you could fetch all the

play01:20

data from the database and load it in if

play01:22

you have a whole lot of users visiting

play01:23

that website the traffic to your

play01:25

database is going to be insane so it's

play01:28

really inefficient instead we can use

play01:30

nextjs to statically build the page so

play01:34

what this means is that it takes the

play01:35

blog article it takes whatever data it

play01:38

has in this case it's going to be our

play01:39

markdown files and when we build our

play01:41

nextjs site nextjs is going to build all

play01:44

of the sub Pages we need for our website

play01:47

so basically they're all pre-prepared we

play01:49

don't have to fetch any more data and so

play01:51

when a user visits that Blog Page the

play01:54

page is already loaded we don't have to

play01:55

fetch any data and it's infinitely more

play01:58

efficient so this is really good if you

play01:59

want to cut costs on your application

play02:01

it's also absolutely brilliant for

play02:02

search engine optimization not to

play02:04

mention that your pages will also load

play02:06

much much faster now in this tutorial

play02:08

we're going to be building a recipe blog

play02:11

app here it's called the bubbly Baker we

play02:13

have a whole lot of recipes these are

play02:15

all saved in markdown files which we'll

play02:17

learn how to access later and if we

play02:20

click on a recipe we get taken to the

play02:22

recipe sub route so the recipe blog sub

play02:25

route and here we can access the whole

play02:27

recipe we can see that the page meta

play02:29

information is updated we can route back

play02:32

to the homepage super fast really quick

play02:35

and very easy to make with nextjs so

play02:38

overall it's going to be an absolute

play02:39

winner of a tutorial if you enjoy the

play02:41

video smash the like And subscribe

play02:42

buttons love that support and with that

play02:44

all said let's get into the code so it's

play02:47

now time to get our hands dirty with

play02:49

some programming and the first thing

play02:51

we're going to do is open up a terminal

play02:53

or a command line and I'm in my project

play02:56

directory where I have all of my

play02:57

projects and this is where we're going

play02:59

to initial iiz our nextjs project now

play03:02

just before we do that there's a few

play03:03

things you should know first off if

play03:05

you're not that familiar with react the

play03:06

react JavaScript framework there's a

play03:08

link to a tutorial in the description

play03:10

down below that will bring you up to

play03:11

speed it does help for building nextjs

play03:14

applications furthermore if you have any

play03:16

questions you're welcome to ask them in

play03:17

the Discord Channel and finally the link

play03:19

to the GitHub repo is also available in

play03:21

the description down below and we'll

play03:23

need that because the application we're

play03:25

building today is pre-styled so we're

play03:27

going to focus on learning nextjs and we

play03:29

don't have to worry about styling our

play03:31

whole application so the CSS file for

play03:33

the project will be available in the

play03:34

GitHub but we will get to that together

play03:36

very shortly now to actually initialize

play03:38

a nextjs project the first thing we're

play03:41

going to do is head over to the nextjs

play03:43

documentation also linked in the

play03:45

description down below and here we are

play03:47

going to use the command npx create next

play03:51

app at latest so if we come back into

play03:54

our terminal we can type that in npx

play03:58

create-- app at latest and then we can

play04:02

name our app so I'll call this project

play04:04

static recipe blog if we hit enter

play04:08

that's going to prompt us for some

play04:10

configuration for our project do we want

play04:13

typescript absolutely not es lint yes

play04:16

tailin CSS this is not going to be a

play04:18

tailin CSS project Source directory no

play04:21

we do not want that the app router yes

play04:24

we do and The Last One Import Alias and

play04:27

that's going to be a

play04:28

no so that will go ahead and install our

play04:32

nextjs project for us and now that that

play04:34

is all done I can go ahead and open up a

play04:36

vs code window Visual Studio code window

play04:40

this is where we'll be doing all of our

play04:41

programming if you don't have this

play04:43

installed a link to install vs code will

play04:45

be in the description down below and in

play04:46

here we're going to head open we're

play04:48

going to come over to our projects

play04:50

directory click on the static recipe

play04:53

blog and hit open that's going to bring

play04:56

our project up for us here we can see

play04:58

all of the files and and the folders

play05:00

that we will be needing for our next

play05:02

year's project so just to quickly run

play05:04

through our directory here we have an

play05:06

app folder this is where all the magic

play05:08

in our project is going to happen we've

play05:09

got some special Pages notably the

play05:12

page.js and the layout. JS we also have

play05:16

a modular CSS sheet a page. module. CSS

play05:19

we're going to go ahead and delete that

play05:21

file cuz we do not need it additionally

play05:23

we have a node modules file we won't be

play05:25

touching that we have a public directory

play05:27

where we can save any local assets and

play05:30

then we just have some configuration

play05:32

files such as our package.json and a

play05:34

readme which also happens to be written

play05:36

in markdown so isn't that handy dandy

play05:38

now the first things we're going to do

play05:40

in our project is create a folder called

play05:45

recipes we're also going to create a

play05:48

folder called

play05:49

utils and we're going to create one more

play05:52

folder called

play05:54

components now all of these folders are

play05:57

created in the top level directory so

play05:59

not inside of the app we can see they're

play06:01

all setting adjacent here and they're

play06:03

just going to be for organizing all of

play06:05

our code if we open up our app we can

play06:08

come in here and we have a pre-styled

play06:10

home route we're going to go ahead and

play06:13

delete everything in the in the page.js

play06:17

route so all we're left with is a basic

play06:20

react functional component here we have

play06:23

a function it's called home and it

play06:26

returns something where this something

play06:27

is known as jsx

play06:31

so in here we'll have our main tags and

play06:33

that's going to be this file for the

play06:35

minute now the next thing we're going to

play06:36

do is we're going to download all of the

play06:39

markdown files I'm not going to get you

play06:41

to write out all of the recipes I

play06:43

prepared them for you in addition to all

play06:45

of the styles for our project if we come

play06:48

into the global. CSS we can delete

play06:51

everything and what I'll want you to do

play06:53

is go over to the GitHub repo that's

play06:55

Linked In the description down below if

play06:58

you click on that and find the the app

play06:59

directory you'll find the global. CSS

play07:02

file and that will have all of the

play07:03

styles that you can just copy or you can

play07:05

download the file directly and save it

play07:07

inside of the app directory so here I'm

play07:10

going to copy everything across and now

play07:13

my project is completely styled and we

play07:15

don't have to stress about

play07:16

it also if you're in the GitHub repo if

play07:19

you could start it that would be amazing

play07:21

super appreciated and while you're there

play07:23

the other thing you'll want to get is

play07:25

the six markdown files you'll find

play07:28

within the recipes folder I'm just once

play07:31

again going to go ahead and copy them

play07:33

across we have six in here we have a

play07:36

markdown formated recipe with some

play07:38

metadata up the top and as I mentioned

play07:41

you can just copy them straight across

play07:42

from the GitHub as well now these recipe

play07:44

files are the blog posts so you could

play07:47

make a whole lot of extra blog posts you

play07:49

can continually add them to this recipes

play07:52

file you could create different

play07:54

subcategories of blog posts and

play07:56

essentially you just make one you add

play07:58

the metadata and you write out your blog

play08:00

article using the markdown syntax now if

play08:03

you're not familiar with the markdown

play08:04

syntax I also have a video on that

play08:06

Linked In the description down below

play08:07

that you could go and check out it's

play08:08

about 8 minutes or something really

play08:10

short it's a very nice syntax to learn I

play08:13

use it for all sorts of note taking and

play08:15

I'd recommend packing it up it doesn't

play08:16

take long so now that we've done all of

play08:18

our admin for our application the next

play08:21

thing we're going to do is go ahead and

play08:22

open up a terminal within our Visual

play08:25

Studio code window now one way you can

play08:27

do this is by hitting control shift p

play08:30

and the menu will come up the top and

play08:33

you can hit toggle terminal and that

play08:34

will pop it up you should also be able

play08:36

to open up a terminal from the file or

play08:39

folder options available once we have

play08:42

this terminal up and running we can come

play08:44

in here and type npm run Dev hit enter

play08:48

and that's going to boot up our app on

play08:51

probably Local Host 3000 VI it's going

play08:53

to be 3,2 for me and we can come back

play08:56

over to a browser and we can paste that

play08:59

URL in and if I move this window to the

play09:02

side we can see that we have a totally

play09:04

blank application perfect just the way

play09:06

we want it now currently this blank

play09:08

application is loading in our Global CSS

play09:11

Styles into the layout we can see here

play09:14

we have imported the global. CSS file up

play09:17

the top and the first thing to note is

play09:19

that this app directory represents the

play09:22

routes and sub pages of our

play09:25

application so at the top level of our

play09:28

app Direct

play09:30

we have two special files we have a

play09:32

layout and we have a page now the page

play09:35

and layout keywords or file names are

play09:38

reserved in nextjs they have special

play09:41

meaning with the layout is a wrapper of

play09:44

our page so that's where we're going to

play09:46

wrap our entire page at this particular

play09:49

path so the Home Path and if we open

play09:51

this up we can see in here we have the

play09:53

body and inside here we have some

play09:55

children contents now the children is

play09:58

everything contained within our layout

play10:01

specifically the page so the page gets

play10:03

rendered within the layout now once

play10:05

again these are both just functional

play10:07

components here we have a function

play10:08

called root layout it receives the

play10:10

children contents which is just whatever

play10:12

it contains whatever it wraps and that's

play10:14

just everything in the page now we're

play10:17

also going to create a new folder inside

play10:20

of the app directory and this is going

play10:21

to be called recipes and in here we're

play10:24

going to create another subfolder and

play10:27

this is going to have square parentheses

play10:30

because it's going to be a dynamic path

play10:32

name and we're just going to call it

play10:35

slug because it's a dynamic path Slug

play10:38

and in here we're going to right click

play10:40

and we're going to create a new page.js

play10:43

file and this page.js file is what will

play10:46

be rendered when we get to the app SL

play10:49

recipes SLS slug whatever that Dynamic

play10:52

slug is route and we'll see how that

play10:54

works in a second in here we're just

play10:57

going to create a default default

play10:59

functional component and I'm going to

play11:00

call this recipe page and we're just

play11:03

going to return a div that says hello

play11:07

world now just to really clarify the way

play11:09

that this works here we have the app

play11:11

directory and if I come into the page.js

play11:14

and type some

play11:15

ASDS refresh the page we see that's what

play11:18

shows up on our home page now instead if

play11:22

I come over and I type SL

play11:27

recipesbanana muffin

play11:29

we can see that hello world gets

play11:31

rendered on the page so that is matching

play11:34

the pathing that we're doing inside of

play11:37

the app directory and the page is what's

play11:40

being rendered within that directory

play11:42

within the layout now the layout wraps

play11:44

our entire application it's just the

play11:46

pages that we add at these specific

play11:48

directories you can technically have

play11:50

nested layouts but I wouldn't worry

play11:51

about that at the minute now one quick

play11:53

thing I'm going to do is just rename

play11:55

this file here instead of recipes I'm

play11:58

just going to call it re recipe cuz it

play11:59

will be a singular recipe that we render

play12:02

and we can see that now broke our little

play12:04

page right here until I once again match

play12:06

the paths

play12:08

up so that's pretty cool we've set up

play12:10

the pages for our application the next

play12:12

thing we're going to do is install some

play12:15

dependencies for our

play12:17

project namely we're going to have npm

play12:20

install inside of our terminal the first

play12:22

one's going to be gray- matter and then

play12:26

we're going to have a space and we're

play12:27

going to install another one called

play12:29

markdown D2

play12:31

djsx and essentially gray matter is

play12:34

going to be used to read the contents of

play12:36

our markdown file and then the markdown

play12:39

jsx is going to take the markdown

play12:41

content that's been read onto our page

play12:44

and convert it into jsx which is

play12:46

essentially what react and nextjs render

play12:48

onto the page so that's pretty

play12:49

straightforward and with that installed

play12:51

we can go ahead and boot up our

play12:53

application Again by typing npm runev

play12:56

now the first component we're going to

play12:58

be working on is the the layout. JS it

play13:00

wraps our application as I said before

play13:02

where the children content that gets

play13:04

wrapped Within These curly parentheses

play13:06

is basically JavaScript that we can

play13:08

render inside of this HTML section and

play13:11

that's just going to display everything

play13:14

that gets wrapped by this component

play13:16

inside of here inside of the function

play13:18

I'm going to create a variable called

play13:20

header and that's going to have some

play13:22

circular parentheses and inside here I'm

play13:24

going to have the header tags opening

play13:26

and closing tags and then I'm going to

play13:29

do the exact same for footer so let

play13:32

footer equal to and then I'm going to

play13:33

have some footer tags as well now if we

play13:36

enter this onto a new

play13:39

line have the children in the middle

play13:42

currently the children is going to be

play13:44

these main tags because our layout wraps

play13:46

the page the page Returns the main so

play13:49

the main is what becomes the children

play13:51

above the children Contents I can now

play13:53

use the circular parentheses to render

play13:55

out my header which is just what we

play13:57

created above and beneath I'm going to

play14:00

render out my footer now if I come in

play14:03

here and type all sorts of random stuff

play14:05

like so and if we come back to the home

play14:07

R we can see that that information does

play14:10

in fact show up it's a shame I use the

play14:12

similar keywords for all of them because

play14:13

it obviously looks very similar but we

play14:16

now have our header at the top of the

play14:17

page the main content in the middle and

play14:19

the footer down the bottom we can do the

play14:21

footer first that's pretty

play14:22

straightforward it's just going to be a

play14:24

paragraph tag that says made with and

play14:27

then we're going to use a little heart

play14:29

emoji just cuzz that's

play14:31

cute I might actually go for a yellow

play14:34

heart it's a bit less

play14:36

intimate uh where is it right there so

play14:39

it's pretty fun that shows up down the

play14:41

bottom nice and easy perfect made with

play14:44

love the next one we're going to do is

play14:46

the header now the header is going to be

play14:48

an H1 tag except we're going to wrap it

play14:52

inside a link tag where the link is a

play14:54

nextjs specific tag so it has a capital

play14:57

letter if we hit enter on the

play15:00

intellisense that's going to import the

play15:02

link component up the top of our

play15:04

page now if we create the opening and

play15:07

closing tags we're going to want to wrap

play15:09

our H1 that's going to say the bubbly

play15:12

bacer that's the name of our recipe

play15:15

app and we can see our application's not

play15:18

happy and that's because a link which

play15:20

works in the same way as an anchor tag

play15:22

does needs an hre and this is just going

play15:25

to be a slash now the reason we're doing

play15:28

this is because our header wraps our

play15:31

entire component our entire web page and

play15:34

since it's inside of this layout it also

play15:36

wraps any subdirectory pages so when I

play15:40

consequently come over to SL recipe SL

play15:44

whatever the name of the recipe is we

play15:46

can see the header and the footer are

play15:49

still here so they wrap our entire

play15:52

application even if the contents within

play15:54

the pages changes and the benefit of

play15:57

doing it like this is now now we created

play15:59

a link where the link just has an hre

play16:02

that's a forward slash that's the Home

play16:05

Route this is available on any page and

play16:08

so now if I click it currently we're on

play16:10

the recipe ASD route if I hit that it

play16:14

takes us back to the homepage so that's

play16:16

just really easy super simple a nice way

play16:19

of adding intuitive navigation to our

play16:21

web

play16:22

page so now that we have our header and

play16:24

our footer done the next thing we're

play16:26

going to do is start getting busy with

play16:28

the actual recipe cards on the homepage

play16:31

and for this there's a few things we're

play16:32

going to do the first thing is creating

play16:35

a component and this is going to be

play16:37

called recipe well actually I'm going to

play16:39

call it postcard. JS and here we're

play16:43

going to export a default function

play16:45

called postcard where the post and card

play16:48

are going to be capitalized and in here

play16:50

we're going to return some jsx this is

play16:53

going to be another nextjs link so we're

play16:57

going to have to import that up the top

play16:59

of our

play17:01

component and in here we can just for

play17:03

the minute provide it an empty hre so

play17:05

it's just an empty string so that it is

play17:07

happy now we'll fill that out more in a

play17:09

second but the next thing we have to do

play17:11

is actually get all the posts read them

play17:15

from our local files inside of this

play17:18

recipes folder and we're going to do

play17:20

that by creating a function inside of

play17:23

utils called get poost

play17:26

metadata. JavaScript .js now inside of

play17:30

this it's not a functional component we

play17:32

didn't give it a capitalized name so

play17:35

it's so it's just a regular Javascript

play17:37

file we need to import a few things

play17:40

namely the file system module so we're

play17:43

going to say import FS from fs and we'll

play17:47

also need to import mattera from Gray

play17:50

matter which is how we're going to read

play17:52

all of our local

play17:54

files after that we're going to define a

play17:56

function called get post

play17:59

metadata and that's going to take a base

play18:02

path and in here what we're going to do

play18:04

once we've opened up that function is

play18:06

we're going to say const and we're going

play18:08

to define the folder where we look for

play18:10

these posts and that's going to be equal

play18:12

to the base path that we pass in plus a

play18:15

forward slash after that we're going to

play18:18

access all of the files so we're going

play18:20

to say cons files and that's going to be

play18:22

equal to file system. readdir sync

play18:26

synchronously and we're going to pass

play18:28

the folder and then after that we're

play18:30

going to read only the markdown files so

play18:33

we're going to say const markdown post

play18:36

is equal to files. filter and we're

play18:39

going to use the array filter method and

play18:41

this is going to take a file and we're

play18:44

going to use an arrow function and we're

play18:46

only going to include a file that ends

play18:49

with theark down extension file

play18:53

extension so now that we have access to

play18:55

all of the posts we can get the file

play18:59

data using the gray matter Library so

play19:02

I'm going to say const posts is equal to

play19:05

all of the markdown post and I'm going

play19:06

to use the map method where the map

play19:09

method takes an arrow function as an

play19:11

argument and in here we're going to get

play19:13

the file name so that's the name of the

play19:15

file and now we're going to say const

play19:18

file contents is equal to fil system.

play19:23

read file synchronously then we're

play19:27

inside of this we're going to call it an

play19:28

AR we're going to call it as an argument

play19:30

and pass in a template literal string

play19:33

and that's going to be the base path SL

play19:36

the file name now the base path and the

play19:40

file name are both wrapped in the dollar

play19:42

sign Cy parenthesis cuz that's how we

play19:44

pass in some JavaScript to our template

play19:46

literal and then we're going to pass in

play19:49

the file encoding which is just going to

play19:51

be

play19:53

utf8 just like that very easy once we

play19:56

have all the file contents now we can

play19:58

say const matter result is equal to and

play20:01

we can call matter now we imported

play20:05

matter from the gray matter library and

play20:07

we can just pass in the file contents

play20:10

and once we have that we can return an

play20:12

object this is what gets passed into our

play20:14

actual

play20:16

component and we just want to access all

play20:18

of the meta information about our

play20:21

recipes so if we come in here we can see

play20:24

the meta information is available

play20:27

between these triple dashes so we have a

play20:29

title a prep time a cook time and a

play20:33

description so we're going to get access

play20:35

to all of them we're going to say title

play20:37

is the key and the value associated with

play20:40

that is matter result. dat. tile make

play20:45

sure we spell that

play20:46

correctly after that we're going to have

play20:49

the prep time and that's going to be

play20:51

matter result. dat. prep time then we'll

play20:56

have the cook time that's going to be

play20:58

matter res. dat. cook time the next one

play21:02

is bio and that's going to be matter.

play21:05

data.

play21:06

description and then finally we're going

play21:08

to have the slug we're going to return

play21:10

the slug which is just going to be the

play21:12

file name. replace and we're going to

play21:15

replace the MD with an empty string just

play21:19

so it looks a bit pretty now up the top

play21:21

here we're going to export uh this

play21:24

function as the main export from this

play21:27

file so we're just going to add the

play21:28

export default in front of the function

play21:31

definition like so and the one last

play21:33

thing I forgot to do is after posts we

play21:36

just have to come down and return posts

play21:40

from the function so now that we've done

play21:43

that we can come back into our page

play21:45

right here this page.js where we have

play21:47

our home functional component and we can

play21:50

get access to all of our posts so we can

play21:53

say cons post metadata is equal to get

play21:58

post metadata we can see that import

play22:01

showing up right here so if I hit enter

play22:03

on that intellisense it's going to

play22:05

create the necessary import for us and

play22:08

now I can call that as a function and

play22:11

pass in the directory where I should be

play22:13

looking for these markdown posts so if I

play22:17

save that and now if I come in here and

play22:19

say console.log post

play22:22

metadata we can see that on the server

play22:25

side we've accessed all of the post

play22:28

metadata that we can Now display on our

play22:30

screen so that's super

play22:32

convenient it's a nice list so what I

play22:35

can do in here is I can create a div

play22:38

that has a class name of post

play22:40

container where post container is a

play22:43

style that's already created inside of

play22:44

the global CSS and in here I can use the

play22:47

circular parentheses and I can map out

play22:50

some post cards so that's going to be

play22:52

cool or recipe cards so I can say post

play22:55

metadata. map

play22:59

and I can call this one the post I can

play23:01

get the post index as the second

play23:03

argument and that is our Arrow function

play23:06

and from the arrow function I can return

play23:09

and in here I can just return the post

play23:13

card or the blog card or the recipe card

play23:16

whatever you should call it and whenever

play23:18

we use a map whatever we return the top

play23:20

level needs to have a unique key which

play23:23

is why we got the post index in as an

play23:26

argument in our Arrow function now I'm

play23:28

also going to add the recipe or the post

play23:31

as a prop to the postcard component so

play23:35

that we can access it within the

play23:36

postcard and I'm now going to remove

play23:38

that console log So currently not much

play23:41

shows up but that's because our postcard

play23:42

is pretty empty we've passed in some

play23:45

props so we need to receive some props

play23:47

and in here what I'm going to do is

play23:50

create this component so first up we're

play23:53

going to say const post is equal to

play23:56

props so we're going going to

play23:58

destructure post out of props which is

play24:00

going to be the object that contains our

play24:04

metadata for that particular recipe card

play24:07

inside of the link we're going to create

play24:10

a div this is going to have an H3 in

play24:13

it now the link itself is going to have

play24:16

a class name of unstyled so we can get

play24:18

rid of any weird link styling the parent

play24:22

or container div is going to be called

play24:24

post

play24:26

card uh where the C has a capital

play24:30

c inside of here we're going to have the

play24:32

H3 then we can have the curly

play24:34

parentheses to render out some

play24:36

JavaScript this is going to contain the

play24:38

post title underneath that we're going

play24:40

to have a paragraph tag that's going to

play24:43

have the post bio beneath that we're

play24:46

going to have a div with the class name

play24:48

of stats

play24:51

container and in here we're going to

play24:53

have another div and this is going to

play24:55

have an H5 that says uh uh prep

play25:01

time like so beneath that we have a

play25:04

paragraph that's post.

play25:08

prepore time and then what I'm going to

play25:11

do is copy and paste this element just

play25:14

underneath and change this to say cook

play25:16

time likewise just here I'll replace

play25:19

that to say

play25:21

cook so if we go ahead and save that

play25:24

just like that we have created this card

play25:26

that shows up and if I click on it

play25:28

currently nothing is going to happen

play25:30

because it's just routing us to the

play25:31

homepage but in a second we're going to

play25:33

provide an hre and that's going to

play25:35

actually rout us to the necessary page

play25:37

now this is pretty cool it's nice and

play25:39

responsive looks sufficient and once

play25:41

this project is complete I challenge you

play25:43

to go out and restyle it if you really

play25:45

want to practice your skills and become

play25:47

a bit an xjs developer adding your own

play25:49

flare and adapting the code in a

play25:51

tutorial is a great way to learn and

play25:54

gain confidence programming by yourself

play25:56

if you do that I'd love to see what you

play25:57

guys guys come up with so you can always

play25:58

share that with me in the Discord

play26:00

Channel now the other thing we have to

play26:01

do is come back and handle this href

play26:04

right here that's pretty straightforward

play26:06

that's just going to Route us to we're

play26:10

going to use a template literal string

play26:13

and that's going to be SL recipe SL

play26:16

dollar sign curly parentheses and that's

play26:19

going to be post.

play26:22

slug so now if I click on this card that

play26:25

takes us to this subsequent route then

play26:27

if I click on the bubbly Baker I get

play26:29

routed back so that's pretty nifty now

play26:32

we haven't really Incorporated any

play26:34

special nextjs features just yet but we

play26:36

have got the routing all working and so

play26:38

with that all done we can come over to

play26:40

our page.js now inside of here a fair

play26:42

bit of magic is going to happen so that

play26:45

everything is statically

play26:47

generated the first thing we're going to

play26:49

have to do is import a bunch of stuff so

play26:51

we're going to import markdown from

play26:53

markdown to jsx we're going to import

play26:56

get post post metadata from our utils

play27:00

we're going to import react from react

play27:04

we'll also need to import the file

play27:06

system from file system and finally

play27:09

we'll import matter from Gray

play27:13

matter now the next thing on our list to

play27:15

do is create a function called get post

play27:19

content and this is just going to be a

play27:21

utility function that takes a slug and

play27:25

it fetches the content from the markdown

play27:27

post that matches the slug so that's why

play27:29

we passing a slug and now what we can do

play27:31

is we can say const folder set that

play27:34

equal to recipes then a forward slash

play27:37

now we can Target the file the file is

play27:40

going to be at the route of the folder

play27:42

plus the back text we can have the

play27:45

slug right here and a template literal

play27:48

string and we can add on the markdown

play27:51

extension and now what we can do is

play27:53

access the content so that's going to be

play27:55

equal to file system. readfile

play27:58

synchronously we can access the file and

play28:00

pass in the encoding which is

play28:04

utf8 and now that we have the contents

play28:06

we can say const the mattera

play28:09

result pass the file say that's equal to

play28:12

mattera and just pass in the

play28:15

content finally we can return the

play28:18

mattera result which is just all of our

play28:20

markdown content almost as raw text so

play28:23

that's pretty cool that's going to get

play28:24

the content from the post now the next

play28:26

two function we're going to Define are

play28:28

going to make this page statically

play28:31

generated we're going to get all of the

play28:33

information we need at build time so

play28:35

that we can build these static pages and

play28:37

we'll have a second function that's

play28:38

going to ensure we have all the

play28:40

necessary meta information for the page

play28:43

as well so that we have good search

play28:45

engine optimization and all that good

play28:47

stuff so the first function we're going

play28:50

to Define is going to be called export

play28:52

const

play28:53

generate static params and that's going

play28:57

to be equal to an asynchronous function

play28:59

note that I'm using the arrow syntax

play29:02

here and in here we're going to say

play29:04

const post is equal to get

play29:07

post metadata we're going to pass in the

play29:11

recipes directory as an

play29:13

argument we imported that function from

play29:15

above and now what we're going to do is

play29:18

we're going to return post. map post

play29:24

this takes an arrow function that

play29:25

receives the post and then we're going

play29:27

to return the circular parentheses with

play29:29

an object so the object is what's

play29:31

getting returned and that's going to

play29:33

have a slug that's called post. Slug and

play29:36

essentially what's going to happen is

play29:38

when we build the pages it's going to

play29:40

get all of the posts and then it's going

play29:43

to create a URL or a page for every post

play29:47

slug that it finds so essentially we'll

play29:50

end up with static pages for all of

play29:52

these directories right here and these

play29:54

are going to be the relevant slugs so

play29:56

that's pretty cool we can have it

play29:57

generate a whole lot of static Pages for

play30:00

us the next thing we're going to do is

play30:01

Define an asynchronous function and this

play30:04

is going to be called generate metad

play30:07

data now this is back to our good old

play30:10

syntax from before and this is going to

play30:12

allow us to get the metadata for the

play30:15

page and here we're going to receive the

play30:17

pams and the search pams now notice I've

play30:20

wrapped them inside of the curly

play30:22

parentheses which means that they're

play30:23

getting destructured from whatever

play30:25

argument is passed into the function

play30:28

inside of this regular function we're

play30:30

going to get the ID so the ID is going

play30:33

to be equal to the page parameters then

play30:36

we're going to use the optional chaining

play30:38

syntax to check for a slug if the slug

play30:41

does exist then we're going to return

play30:44

something otherwise we're going to

play30:46

return something else so in the case

play30:47

where the slug does exist just like up

play30:50

here we're going to return the

play30:52

multiplication dot I like using that and

play30:55

then we're going to add on on to that

play30:58

the params and the slug the Turner

play31:01

Operator just here is a conditional

play31:03

check so this check basically says all

play31:06

right we want to define the ID we're

play31:08

going to check if the slug exists if it

play31:11

does we're going to assign this section

play31:13

right here to the ID otherwise we're

play31:15

just going to assign an empty string

play31:17

after that we're going to return and the

play31:20

title of the page is going to be a

play31:22

template literal string and this is

play31:25

going to be the bubbly

play31:28

Baker remember the title is the metadata

play31:31

title so that's what comes up up here

play31:33

and then we're going to have a space and

play31:36

in here we're just going to use the

play31:38

template literal syntax and we're going

play31:41

to throw in the ID and we're also going

play31:44

to replace every instance of an

play31:47

underscore with a space so that we don't

play31:50

get any weird underscores from these

play31:51

file names right here so that's going to

play31:53

generate all of the metadata for the

play31:56

static pages that we want to build all

play31:58

of the static Pages we basically have a

play32:00

static page or we return a static page

play32:03

for every markdown recipe and our

play32:05

recipes folder now we can actually

play32:07

Define the contents that's going to go

play32:09

inside of the statically generated Pages

play32:11

the pages that get built in build time

play32:14

and are saved cached so that they're

play32:16

built once they can be used a whole lot

play32:17

and we only have to get the data once so

play32:20

the first thing we're going to do is

play32:21

receive some props inside of our recipe

play32:25

page and from the props we can get the

play32:28

slug which is the name of this

play32:29

particular page so the slug is equal to

play32:32

the props do the parameters of the page

play32:35

do slug so once we have the slug that's

play32:39

basically this fell right here slug

play32:41

whatever it is that end URL that end

play32:44

path that we're at once we have that we

play32:46

can say con post and that's equal to get

play32:49

post content which is this function we

play32:52

defined up the top right here that takes

play32:55

the slug and that's going to get the

play32:56

content that's literally inside of that

play32:59

post so we can just pass in the

play33:02

slug and now we should be able to

play33:05

console.log the post and see all that

play33:08

information pop up so if I inspect our

play33:11

page and come in here and we click on

play33:12

that link nothing's happening and that's

play33:14

because it's a server side page so down

play33:17

here we can see we have all of the uh

play33:19

contents so the console is on the server

play33:22

side in inside of our terminal so that's

play33:25

pretty cool we have all of this uh

play33:27

content right here that we can now go

play33:29

ahead and access and so what we're going

play33:31

to do is we're going to come in here

play33:33

we're going to swap these dibs out for

play33:35

Mains because this is the file that gets

play33:39

wrapped by by our header and footer we

play33:41

essentially have to match the main

play33:43

syntax so it's the main body of our

play33:45

document on this page this is also going

play33:47

to be the main body of our document

play33:50

inside here we're going to have an

play33:52

article tag because it's semantically an

play33:54

article and inside of here we're going

play33:57

going to render out the markdown which

play33:59

we imported above from the markdown to

play34:02

jsx and inside of the markdown tags

play34:05

we're going to render out the post.

play34:07

content if you remember when we looked

play34:09

in here the post. content is all this

play34:12

wacky stuff right

play34:13

here there's all of the content so we're

play34:16

just going to render that out on the

play34:18

page so if we go ahead and save that now

play34:21

we can see our markdown post gets

play34:24

rendered on the page we can also Al see

play34:27

that the metadata for our page is

play34:29

updated so here we have the bubbly

play34:31

baker. applepie that's the subdirectory

play34:34

this is the apple pie if we go back home

play34:37

we get create next app so we'll fix that

play34:39

in a sec and we'll learn how to add

play34:41

metadata to a home

play34:43

route but then if I click on the banana

play34:45

bread the bubbly Baker banana bread

play34:48

route updates so our title our page meta

play34:51

data is all sorted these pages are

play34:53

statically generated so they're built at

play34:55

built time they're saved and then the

play34:57

data is red we don't have to refetch the

play35:00

data every time a new user loads the

play35:02

page so it's incredibly efficient super

play35:04

fast and good for the bank depending on

play35:07

your hosting configuration if we click

play35:09

on the bubbly Baker it routes us back

play35:11

home so that's pretty cool everything is

play35:13

nice and responsive and suddenly you can

play35:16

see how you could make a massive blog

play35:18

site just from this very simple syntax

play35:21

throw in some extra styling add a few

play35:22

more blog posts and you know Bob's your

play35:25

uncle now the one last thing we're going

play35:27

to do before we're done is come back to

play35:28

our layout up here and we're just going

play35:31

to change this page metadata to the

play35:33

bubbly Baker as well so everything

play35:36

matches and the description for this

play35:38

page is going to be my amazing recipe

play35:42

app pretty straightforward and so now

play35:45

the title is updated to match we have

play35:47

the bubbly Baker I can click on one we

play35:48

go to the banana bread I can come back

play35:51

home and we have all of our recipes and

play35:53

just like that we've learned how to make

play35:55

a static blog site with the nextjs we've

play35:58

learned how to statically generate the

play35:59

pages everything is server side rendered

play36:02

so it's incredibly good for search

play36:04

engine optimization it's super fast as

play36:07

well we've also learned about how to

play36:09

make nextjs components we learned about

play36:11

the reserved page and layout files we

play36:14

learned how to create components map out

play36:16

different components fetch all the posts

play36:18

render out the Met information pass

play36:21

through all the markdown and show it on

play36:22

the screen as I mentioned at the start

play36:24

of the video my website is literally the

play36:27

exact same foundational skeleton so you

play36:30

can make all sorts of wicked stuff just

play36:33

from this code we have put together in

play36:35

this tutorial as always I highly

play36:37

recommend you go and make some

play36:38

personalizations to this project change

play36:41

some of the Articles make your own blog

play36:43

site make it look different fiddle with

play36:45

the Styles you could even get rid of the

play36:46

global CSS and add tailwind and style

play36:48

out your application that way that's

play36:50

what my website is that's Tailwind

play36:53

instead as always if you've enjoyed the

play36:55

video smash the like And subscribe

play36:56

buttons thank you guys so much for

play36:58

watching I hope you've learned a lot and

play36:59

I'll catch you in the next video peace

play37:01

learning to code if so be sure to check

play37:03

out the learn to code road map or dive

play37:05

straight in with these

play37:09

videos that's a good

play37:11

one

Rate This
★
★
★
★
★

5.0 / 5 (0 votes)

Related Tags
Next.jsブログスタティックSEOチュヌトリアルMarkdownコヌド開発りェブサむトプログラミング
Do you need a summary in English?