YouTube Icon

Code Playground.

How to Develop a Blog App Using NextJS

CFG

How to Develop a Blog App Using NextJS

In this article, we will utilize Next.js to fabricate a static blog system with the plan and structure propelled by Jekyll. I've generally been a major fanatic of how Jekyll makes it simpler for learners to arrangement a blog and simultaneously additionally gives an extraordinary level of power over each part of the blog for the propelled clients. 

With the presentation of Next.js as of late, joined with the notoriety of React, there is another road to investigate for static online journals. Next.js makes it overly simple to assemble static sites dependent on the record framework itself with practically zero design required. 

Also Read:-How to install Atom editor in Ubuntu

The index structure of a run of the mill stripped down Jekyll blog resembles this: 

???? _posts/          ...blog posts in markdown
???? _layouts/        ...layouts for different pages
???? _includes/       ...re-usable components
???? index.md         ...homepage
???? config.yml       ...blog config

The thought is to plan our system around this index structure however much as could reasonably be expected with the goal that it gets simpler to relocate a blog from Jekyll by just reusing the posts and configs characterized in the blog. 

Establishment 

Next.js is controlled by React and written in Node.js. So we have to introduce npm first, before including straightaway, react and react dom to the undertaking. 

mkdir nextjs-blog && cd $_
npm init -y
npm install next react react-dom --save

To run Next.js contents on the order line, we need to add the following order to the contents segment of our package.json. 

"scripts": {
  "dev": "next"
}

We would now be able to run npm run dev on the order line just because. We should perceive what occurs. 

$ npm run dev
> nextjs-blog@1.0.0 dev /~user/nextjs-blog
> next

ready - started server on http://localhost:3000
Error: > Couldn't find a `pages` directory. Please create one under the project root

The compiler is whining about a missing pages index in the base of the venture. We'll find out about the idea of pages in the following area. 

Idea of pages 

Next.js is worked around the idea of pages. Each page is a React segment that can be of type .js or .jsx which is planned to a course dependent on the filename. For instance: 

File                            Route
----                            -----
/pages/about.js                 /about
/pages/projects/work1.js        /projects/work1
/pages/index.js                 /

We should make the pages registry in the foundation of the extend and populate our first page, index.js, with a fundamental React segment. 

// pages/index.js
export default function Blog() {
  return <div>Welcome to the Next.js blog</div>
}

Run npm run dev by and by to begin the server and explore to http://localhost:3000 in the program to see your blog just because. 

Out of the crate, we get: 

  • Hot reloading so we don't need to revive the program for each code change. 
  • Static age of all pages inside the/pages/** registry. 
  • Static record serving for resources living in/general society/** catalog. 
  • 404 mistake page. 

Explore to an irregular way on localhost to see the 404 page in real life. On the off chance that you need a custom 404 page, the Next.js docs have extraordinary data. 

Dynamic pages 

Pages with static courses are helpful to construct the landing page, about page, and so on. Notwithstanding, to progressively assemble every one of our posts, we will utilize the dynamic course capacity of Next.js. For instance: 

File                        Route
----                        -----
/pages/posts/[slug].js      /posts/1
                            /posts/abc
                            /posts/hello-world

Any course, as/posts/1,/posts/abc, and so forth., will be coordinated by/posts/[slug].js and the slug boundary will be sent as an inquiry boundary to the page. This is particularly valuable for our blog entries since we would prefer not to make one document for every post; rather we could progressively pass the slug to render the comparing post. 

Life structures of a blog 

Presently, since we comprehend the essential structure squares of Next.js, we should characterize the life systems of our blog. 

.
?? api
?  ?? index.js             # fetch posts, load configs, parse .md files etc
?? _includes
?  ?? footer.js            # footer component
?  ?? header.js            # header component
?? _layouts
?  ?? default.js           # default layout for static pages like index, about
?  ?? post.js              # post layout inherts from the default layout
?? pages
?  ?? index.js             # homepage
|  ?? posts                # posts will be available on the route /posts/
|     ?? [slug].js       # dynamic page to build posts
?? _posts
   ?? welcome-to-nextjs.md
   ?? style-guide-101.md

Blog API 

A fundamental blog structure needs two API capacities: 

  • A capacity to get the metadata of the considerable number of posts in _posts index 
  • A capacity to get a solitary post for a given slug with the total HTML and metadata 

Alternatively, we would likewise like all the site's setup characterized in config.yml to be accessible over all the parts. So we need a capacity that will parse the YAML config into a local item. 

Since, we would manage a ton of non-JavaScript documents, as Markdown (.md), YAML (.yml), and so forth, we'll utilize the crude loader library to load such records as strings to make it simpler to process them. 

npm install raw-loader --save-dev

Next we have to advise Next.js to utilize crude loader when we import .md and .yml record arranges by making a next.config.js document in the foundation of the undertaking. 

module.exports = {
  target: 'serverless',
  webpack: function (config) {
    config.module.rules.push({test:  /\.md$/, use: 'raw-loader'})
    config.module.rules.push({test: /\.yml$/, use: 'raw-loader'})
    return config
  }
}

Next.js 9.4 presented nom de plumes for relative imports which assists clean with increasing the import proclamation spaghetti brought about by relative ways. To utilize assumed names, make a jsconfig.json document in the task's root catalog determining the base way and all the module pseudonyms required for the undertaking. 

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@includes/*": ["_includes/*"],
      "@layouts/*": ["_layouts/*"],
      "@posts/*": ["_posts/*"],
      "@api": ["api/index"],
    }
  }
}

For instance, this permits us to import our formats by simply utilizing: 

import DefaultLayout from '@layouts/default'

Bring all the posts 

This capacity will peruse all the Markdown documents in the _posts index, parse the front issue characterized toward the start of the post utilizing dark issue and return the variety of metadata for all the posts. 

// api/index.js
import matter from 'gray-matter'
?
export async function getAllPosts() {
  const context = require.context('../_posts', false, /\.md$/)
  const posts = []
  for(const key of context.keys()){
    const post = key.slice(2);
    const content = await import(`../_posts/${post}`);
    const meta = matter(content.default)
    posts.push({
      slug: post.replace('.md',''),
      title: meta.data.title
    })
  }
  return posts;
}

A regular Markdown post resembles this: 

---
title:  "Welcome to Next.js blog!"
---
**Hello world**, this is my first Next.js blog post and it is written in Markdown.
I hope you like it!

The area sketched out by - is known as the front issue which holds the metadata of the post like, title, permalink, labels, and so forth. Here's the yield: 

[
  { slug: 'style-guide-101', title: 'Style Guide 101' },
  { slug: 'welcome-to-nextjs', title: 'Welcome to Next.js blog!' }
]

Get a solitary post 

For a given slug, this capacity will find the record in the _posts catalog, parse the Markdown with the checked library and return the yield HTML with metadata. 

// api/index.js
import matter from 'gray-matter'
import marked from 'marked'
?
export async function getPostBySlug(slug) {
  const fileContent = await import(`../_posts/${slug}.md`)
  const meta = matter(fileContent.default)
  const content = marked(meta.content)    
  return {
    title: meta.data.title, 
    content: content
  }
}

Test yield: 

{
  title: 'Style Guide 101',
  content: '<p>Incididunt cupidatat eiusmod ...</p>'
}

Config 

So as to re-utilize the Jekyll config for our Next.js blog, we'll parse the YAML record utilizing the js-yaml library and fare this config with the goal that it tends to be utilized across segments. 

// config.yml
title: "Next.js blog"
description: "This blog is powered by Next.js"
?
// api/index.js
import yaml from 'js-yaml'
export async function getConfig() {
  const config = await import(`../config.yml`)
  return yaml.safeLoad(config.default)
}

Incorporates 

Our _includes index contains two fundamental React segments, <Header> and <Footer>, which will be utilized in the diverse design segments characterized in the _layouts catalog. 

// _includes/header.js
export default function Header() {
  return <header><p>Blog | Powered by Next.js</p></header>
}
?
// _includes/footer.js
export default function Footer() {
  return <footer><p>©2020 | Footer</p></footer>
}

Formats 

We have two format parts in the _layouts catalog. One is the <DefaultLayout> which is the base design on head of which each other format segment will be assembled. 

// _layouts/default.js
import Head from 'next/head'
import Header from '@includes/header'
import Footer from '@includes/footer'
?
export default function DefaultLayout(props) {
  return (
    <main>
      <Head>
        <title>{props.title}</title>
        <meta name='description' content={props.description}/>
      </Head>
      <Header/>
      {props.children}
      <Footer/>
    </main>
  )
}

The subsequent format is the <PostLayout> part that will abrogate the title characterized in the <DefaultLayout> with the post title and render the HTML of the post. It likewise incorporates a connection back to the landing page. 

// _layouts/post.js
import DefaultLayout from '@layouts/default'
import Head from 'next/head'
import Link from 'next/link'
?
export default function PostLayout(props) {
  return (
    <DefaultLayout>
      <Head>
        <title>{props.title}</title>
      </Head>
      <article>
        <h1>{props.title}</h1>
        <div dangerouslySetInnerHTML={{__html:props.content}}/>
        <div><Link href='/'><a>Home</a></Link></div> 
      </article>
    </DefaultLayout>
  )
}

Landing page 

As a major aspect of the list page, otherwise known as landing page, we will list all the posts inside the _posts registry. The rundown will contain the post title and the permalink to the individual post page. The list page will utilize the <DefaultLayout> and we'll import the config in the landing page to pass the title and depiction to the design. 

// pages/index.js
import DefaultLayout from '@layouts/default'
import Link from 'next/link'
import { getConfig, getAllPosts } from '@api'
?
export default function Blog(props) {
  return (
    <DefaultLayout title={props.title} description={props.description}>
      <p>List of posts:</p>
      <ul>
        {props.posts.map(function(post, idx) {
          return (
            <li key={idx}>
              <Link href={'/posts/'+post.slug}>
                <a>{post.title}</a>
              </Link>
            </li>
          )
        })}
      </ul>
    </DefaultLayout>
  )
} 
?
export async function getStaticProps() {
  const config = await getConfig()
  const allPosts = await getAllPosts()
  return {
    props: {
      posts: allPosts,
      title: config.title,
      description: config.description
    }
  }
}

getStaticProps is called at the construct time to pre-render pages by passing props to the default part of the page. We utilize this capacity to get the rundown of all posts at fabricate time and render the posts chronicle on the landing page. 

Post page 

This page will render the title and substance of the post for the slug provided as a major aspect of the specific circumstance. The post page will utilize the <PostLayout> segment. 

// pages/posts/[slug].js
import PostLayout from '@layouts/post'
import { getPostBySlug, getAllPosts } from "@api"
?
export default function Post(props) {
  return <PostLayout title={props.title} content={props.content}/>
}
?
export async function getStaticProps(context) {
  return {
    props: await getPostBySlug(context.params.slug)
  }
}
?
export async function getStaticPaths() {
  let paths = await getAllPosts()
  paths = paths.map(post => ({
    params: { slug:post.slug }
  }));
  return {
    paths: paths,
    fallback: false
  }
}

On the off chance that a page has dynamic courses, Next.js has to know all the potential ways at construct time. getStaticPaths supplies the rundown of ways that must be rendered to HTML at construct time. The fallback property guarantees that on the off chance that you visit a course that doesn't exist in the rundown of ways, it will restore a 404 page. 

Creation prepared 

Include the accompanying orders for construct and start in package.json, under the contents area and afterward run npm run manufacture followed by npm run begin to fabricate the static blog and start the creation server. 

// package.json
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

The whole source code in this article is accessible on this GitHub archive. Don't hesitate to clone it locally and mess with it. The store additionally incorporates some essential placeholders to apply CSS to your blog. 

Also Read:-How to Create a React Application and Deploy It on GitHub Pages

Upgrades 

The blog, albeit useful, is maybe excessively essential for most normal cases. It is ideal to expand the structure or present a fix to incorporate some more highlights like: 

  • Pagination 
  • Linguistic structure featuring 
  • Classes and Tags for posts 
  • Styling 

In general, Next.js appears to be actually quite encouraging to manufacture static sites, similar to a blog. Joined with its capacity to send out static HTML, we can constructed a genuinely independent application without the need of a server!




CFG