Special mention to Traversy Media for content that is referenced here.
Next.js Setup and Styling
$ npx create-next-app blog
npm i marked gray-matter
To run (opens default app):
npm run dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait - compiling...
event - compiled client and server successfully in 9s (125 modules)
Deleting the unwanted:
- Not using api routes, so delete
pages/api
folder
index.js
is home page- Each page in Next.js is a functional component, example:
Home
here is a functional component of react.
Anytime you create a component in pages
folder like say about.js
, that will allow us to go to /about
and that component will get automatically rendered - easy routing due to Next.js
Remove some unwanted boilerplate
Keep Head
import because we need the title of page.
Delete this class
because we will have a single global CSS file.
Get rid of main
tag and footer
tag
Change <title>
etc.
Final is very basic:
Add a global css
Get global.css
from here and add it to styles/
Create a components
folder
for components that are not pages, like Header
etc.
Tip: Use ES7 react redux extension for VSCode - do
_rfc
and it will give you a function component.
Use rfc
to create a basic react functional component
Add a div with className “container”
Tip: Enable Emmet for .js files:
Nothing’s going to show up now, because we have not added it yet.
Now go to _app.js
and import Header
and add the Header
in _app.js
Add a <main>
tag with “container” class, and move the Component
into the main
tag
Create a Markdown File
index.js (HOME PAGE) is where we want to use a data fetching method - to fetch our Markdown posts.
The markdown posts are going to go in root in a folder called posts, so lets create that.
Create a folder called posts
on root.
Create a folder images
in public
also create public/images/posts
to store images of your post.
Create a frontmatter - it is always at the top
In the test md file,
Get post on homepage from md:
getStaticProps()
allows us to fetch data at build time - we can use this to create static website - use this to fetch data for static website.
In index.js
export async function getStaticProps() {
// here data can even come from strapi etc.(any headless CMS)
return {
props: { posts: "The Posts" }, //test
};
}
getStaticProps()
makes it available in the main component: (here in Home functional component)
Import fs
Although as soon as you add the import
import fs from "fs";
You will see error by Next
Reason for the error is - fs
is not something you can import on the client side. fs
is the filesystem node.js module and we are trying to import it in client side code (react code).
However next.js is smart enough to know that if you use the fs module inside the getStaticProps()
which will run on Server Side, then fs will work.
No more errors! :)
Even if import fs from "fs"
is still in code, fs
was used only inside getStaticProps() function and hence the error is no longer shown by Next.
1 = array of all files (names?) on posts directory.
2 = 3, they are same.
Create an array that will contain all the slugs.
~15:00 mins in https://youtu.be/MrjeefD8sac?t=895
(For now there is only 1 test.md - its slug)
Use gray-matter
import matter from "gray-matter";
in getStaticProps()
// destructring - renaming the data to frontmatter.
const { data: **frontmatter** } = matter(markdownWithMeta);
return {
slug,
**frontmatter**,
};
Now return the posts
instead of test string, from getStaticProps()
return {
props: { posts: posts },
};
we are logging it in component start
So for log at line 7, we see:
why array? because its result of map()
Also array size = no. of files. We have only 1 test.md
:
Notice that we dont have the body of markdown - we dont need it because we will not put it in
index.js
but will do in another component.
Add some more .md files see them getting displayed.
Create a component for post, instead of the h3
Added links to Post component
It is redirecting to the URL (but getting 404 ofcourse, we dont have those pages yet).
Need to work on inner page now.
We wants url of the blog page to be like /blog/slug
Since this is a static site, we need to create these paths at build time obviously. They way we will do that with specific data fetching method called getStaticPaths
We wants url of the blog page to be like /blog/slug
and slug
can be anything. So we have to format the pages like following, for routing:
So starting:
- Create a new folder
blog
underpages/
- Create a
js
file under blog[slug].js
It is named with because we want to be dynamic. - Create react component in the js file - call it PostPage
export default function PostPage() {
return <div>post</div>;
}
We also want to do a couple of things
- Obviously Fetch the data from the markdown files, for the PostPage
- Statically generate these paths (because this is going to be a static website - and it needs to know paths beforehand, and we need to do that based on the data)
Write 2 functions -
getStaticPath
andgetStaticProps
import fs from "fs";
import path from "path";
export default function PostPage() {
return <div>post</div>;
}
export async function getStaticPaths() {
const files = fs.readdirSync(path.join("posts"));
const paths = files.map((filename) => ({
params: {
slug: filename.replace(".md", ""),
},
}));
console.log(paths);
return {
paths, // paths will be like [{params: {slug: 1}}]
fallback: false, //if doesnt find a page, for 404
};
}
//its incomplete now
export async function getStaticProps() {
return {
props: {},
};
}
paths logged just before return is
Since we are returning paths
(it has the slugs we needed) from getStaticPaths
we can access that in the other function getStaticProps
export async function getStaticProps({ params: { slug } }) {
console.log(slug);
return {
props: {},
};
}
We are able to see the log : (when page 3 was refreshed/loaded) :
So we now have direct access to whatever is the slug is, inside getStaticProps
Now work on content
To see what exactly is frontmatter, JSON.stringify
(we have see in past for the list page)
Btw
<> </>
is called an empty fragment
We add a button on the PostPage to go to home:
We construct the actual page content, using the CSS classes present in the global.css:
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import Link from "next/link";
import { marked } from "marked";
export default function PostPage({
frontmatter: { title, date, cover_image },
slug,
content,
}) {
return (
<>
<Link href="/">
<a className="btn btn-back">Go Back</a>
</Link>
<div className="card card-page">
<h1 className="post-title">{title}</h1>
<div className="postdate">Posted on {date}</div>
<img src={cover_image} alt="post-image" />
<div className="post-body">
{/* will use marked here - will insert the MD HTML here
Basicall inserting HTML - when we do that within JSX we have
to use an attribute called dangerouslySetInnerHTML.
It is set with double curly braces, like {{__html: }}*/}
<div dangerouslySetInnerHTML={{ __html: marked(content) }}></div>
</div>
</div>
</>
);
}
Notice the use of attrib: dangerouslySetInnerHTML
We set it with {{ }}
and __html
as the key.
We notice the dates of posts on the homepage may not be sorted.
Sort it in index.js
import a utility function sortByDate
to help with sorting
We are done with basic blog site creation, now deployment!
Continued in Part 2