Scaffolding the Next.js App
Tags: nextjs, tailwindcss, frontend
Published: 2021-05-28 06:28:25

In this series, I am rewriting the code of my blog (ashutosh.dev) in Next.js and Mojolicious as backend.
This is the second article of the series.

In this article, we cover

- How to configure the storybook
- Configure the TailwindCSS
- Add SEO friendly _document.js
- Create basic components

# Configure Storybook

In our last article, we install and set up the storybook in our Next.js app. The installation process creates a folder name stories. And, under the folder, there are JavaScript files shipped during the installation process. Delete all the files under the stories folder. It'll break the storybook at 6006 port. 

Install the following:

```bash
npm install @storybook/addon-postcss
npm install storybook-css-modules-preset
```

Go to the .storybook folder and open the main.js file and, modify the content of the files as follows:

```javascript
module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-postcss",
    "storybook-css-modules-preset"
  ],
  webpackFinal: async config => {
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
    });
    return config
  }
}
```

Open the preview.js file and update the content as follows:

```javascript
import '../styles/globals.css'
export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
}
```

# Configure TailwindCSS

In our last article, we install and configure the TailwindCSS. Let's quickly add a new theme. 

open tailwind.config.js and modify the theme as

```javascript
theme: {
  extend: {
    colors: {
      'light-blue': colors.lightBlue,
      cyan: colors.cyan,
    }
  },
},
```

Now, the content of the file looks like,


```javascript
const colors = require('tailwindcss/colors')
module.exports = {
  purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {
      colors: {
        'light-blue': colors.lightBlue,
        cyan: colors.cyan,
      }
    },
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```

# Create _document.js

Under the pages folder, create a new file _document.js and add the following content.

```javascript
import Document from "next/document";
import {Html, Head, Main, NextScript} from "next/document";
class AppDocument extends Document {
    render () {
        return (
            <Html lang="en">
                <Head>
                    <meta httpEquiv="Cache-control" content="public" />
                    <meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
                    <meta name="keywords" content="Perl, Python, Mojolicious tutorial, Javascript, Next.js" />
                </Head>
                <body>
                    <Main></Main>
                    <NextScript></NextScript>
                </body>
            </Html>
        )
    }
}
export default AppDocument
```

You can modify the meta-name as per your preference. It's a sample only.  

If you don't know what _document.js is, then follow [this link](https://ashutosh.dev/blog/post/2021/05/how-to-improve-nextjs-seo) to understand more about it.

# Components

We'll create few components and then use those components in our pages. We'll start with the following:

- HeaderComponent
  - NavigationComponent
- CategoriesComponent
  - CategoriesListComponent
- BlogComponent
  - BlogListComponent
- ButtonComponent

We'll create the button component but, we'll use it in the forthcoming articles.

I prefer to divide the app into smaller components. Hence, it's easier to manage and debug it. It has another advantage we'll use it across the application without disturbing the UI.

Create new directory components. And, under the components folder, Create Header, Footer, Blog, Button, Categories folder. 

<img src="https://github.com/akuks/Misc/raw/main/Images/ComponentsFolderScreenshot.png" />


Under the Header folder, create two javascript files:

- HeaderComponent.js
- NavigationComponent.js

Open the NavigationComponent.js and add the following content:

```javascript
function NavigationComponent() {
    return (
        <div className="bg-gradient-to-r from-cyan-400 to-light-blue-500 rounded shadow-lg">
            <nav className="flex flex-wrap items-center justify-between p-4  rounded">
                <div className="block lg:hidden">
                    <button
                        className="navbar-burger flex items-center py-2 px-3 text-indigo-500 rounded border border-indigo-500">
                        <svg className="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
                            <title>Menu</title>
                            <path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path>
                        </svg>
                    </button>
                </div>
                <div className="navbar-menu hidden lg:flex lg:flex-grow lg:items-center w-full lg:w-auto">
                    <div className="lg:mx-auto">
                        <a className="block lg:inline-block mt-4 lg:mt-0 mr-10 font-bold text-white hover:underline"
                        href="/">
                            Home
                        </a>
                        <a className="block lg:inline-block mt-4 lg:mt-0 mr-10 font-bold text-white hover:underline"
                        href="/about">
                            About
                        </a>
                        <a className="block lg:inline-block mt-4 lg:mt-0 text-white font-bold hover:underline"
                        href="/contact">
                            Contact
                        </a>
                    </div>
                </div>
            </nav>
        </div>
    )
}
export default NavigationComponent
```

Now, open the *HeaderComponent.js* file and use the NagivationComponent in it.

```javascript
import NavigationComponent from "./NavigationComponent";
function HeaderComponent() {
    return (
        <div>
            <NavigationComponent />
        </div>
    )
}
export default HeaderComponent;
```

And that's all to your HeaderComponent.

And to verify if your header component is working fine. Create a story inside the stories folder. 

Create a new file Header.stories.js and add the following:

```javascript
import HeaderComponent from "../components/Header/HeaderComponent";
export default {
    title: 'App/AppHeader',
    component: HeaderComponent
}
const Template = args => <HeaderComponent />
// New instance of component
 export const Default = Template.bind({})
```

We are not passing any arguments to the story because it's unnecessary at present. Maybe we'll add in the forthcoming articles.

Now, run

```bash
yarn storybook
```

In the storybook URL, you'll see AppHeader under the App folder. When you click on the AppHeader, it'll look like the below screenshot.


<img src="https://github.com/akuks/Misc/raw/main/Images/StoryScreenshot.png" />


# Categories Components
Create folder categories under the components folder.

We will create two components primary component always returns the list of the categories (CategoriesItem.js file). We'll also create another component that uses the categories-item component. Let's do that quickly. 

Create a file *CategoriesItem.js* and add the following code:

```javascript
import Link from "next/link";
function CategoriesItem() {
    const Categories = ['Perl', 'Python', 'Data Analysis', 'Mojolicious', 'NextJs']
    return (
        <div className="h-full-screen shadow-lg rounded">
            <ul className="ml-4 mb-4 max-w-full mr-4">
                <li>/#> ls</li>
                {
                    Categories.map(event =>
                        <div key={event}>
                            <li className="mt-2 mb-2 mr-4 py-4 px-4" id={event} >
                                - <a className="hover:underline hover:text-cyan-600" href="#"> {event} </a>
                            </li>
                            <hr />
                        </div>
                    )
                }
            </ul>
        </div>
    )
}
export default CategoriesItem
```

Now we'll create *CategoriesComponent.js* and add the following code:

```javascript
import CategoriesItem from "./CategoriesItem";
function CategoriesComponent() {
    return (
        <div className="ml-10 mt-4">
            <CategoriesItem />
        </div>
    )
}
```

Now, it completes our categories component. Let's update the story for it. 

Under the stories, creates a file *Categories.stories.js*. Add the following code:

```javascript
import CategoriesComponent from "../components/Categories/CategoriesComponent";
export default {
    title: 'App/Categories',
    component: CategoriesComponent
}
const Template = args => <CategoriesComponent />
// New instance of component
 export const Default = Template.bind({})
```

If the storybook is already running, then you'll see Categories under the APP. 

# Button Component

We'll not use the Button component in this article. However, we need it in our upcoming sections/articles.

Let's create a primary button now.

Create a folder Button under the components directory.

And add the file *Button.js*. Update the file with the following:

```javascript
const Button = ({size, children}) => {
    return(
        <button className={` bg-gradient-to-r from-cyan-400 to-light-blue-500 font-bold text-white px-6 py-2 rounded hover:bg-green-500 ${size} `}>
            {children}
        </button>
    )
}
export default Button;
```

Update the story now. 

Create a file *Button.stories.js* under the stories. And add the following code:

```javascript
import Button from "../components/Button/Button";

export default {
    title: 'UI/Control/Buttons'
}

const Template = args => <Button {...args} />

export const Primary = Template.bind({})

Primary.args ={
    size: "h-10 w-56",
    children: "Primary Button"
}
```
Open the storybook and, you'll see the button under the UI/Control/Buttons.

# Blog Components

So far, we have created a basic navigation header, categories for our app. It's time to add the layout of the Blog component. 

Create a folder Blog under the components folder. Add BlogComponent.js and BlogList.js file.


In the *BlogList.js* file add dummy content and update the file with the following:

```javascript
function BlogList() {
    return (
        <div className="ml-4">
            <div className="border-b shadow rounded-md hover:shadow-lg hover:border">
                <div className="ml-4 mt-4">
                    <h1 className="font-bold text-2xl">It's All About (The) Next.js api routes</h1>
                </div>
                {/*Posted Date*/}
                <div className="ml-4 mt-4 flex">
                    <p>
                        <span className="text-gray-400">Posted On </span>
                        <small className="hover:underline hover:text-cyan-500">May 22, 2021</small>
                    </p>
                    {/*Categories Belongs to*/}
                    <div className="ml-10">
                        <span className="inline-block bg-cyan-200 text-cyan-800 text-xs px-2 rounded-full uppercase font-semibold tracking-wide mr-2">
                            Perl
                        </span>
                        <span className="inline-block bg-cyan-200 text-cyan-800 text-xs px-2 rounded-full uppercase font-semibold tracking-wide mb-10">
                            Nextjs
                        </span>
                    </div>
                </div>
            </div>
            <div className="border-b shadow rounded-md hover:shadow-lg hover:border">
                <div className="ml-4 mt-4">
                    <h1 className="font-bold text-2xl">Develop a blog in Next.js and Mojolicious - (Part - 1)</h1>
                </div>
                {/*Posted Date*/}
                <div className="ml-4 mt-4 flex">
                    <p>
                        <span className="text-gray-400">Posted On </span>
                        <small className="hover:underline hover:text-cyan-500">May 22, 2021</small>
                    </p>
                    {/*Categories Belongs to*/}
                    <div className="ml-10">
                        <span className="inline-block bg-cyan-200 text-cyan-800 text-xs px-2 rounded-full uppercase font-semibold tracking-wide mr-2">
                            Perl
                        </span>
                        <span className="inline-block bg-cyan-200 text-cyan-800 text-xs px-2 rounded-full uppercase font-semibold tracking-wide mb-10">
                            Nextjs
                        </span>
                    </div>
                </div>
            </div>
            <div className="border-b shadow rounded-md hover:shadow-lg hover:border">
                <div className="ml-4 mt-4">
                    <h1 className="font-bold text-2xl">It's All About (The) Next.js Api routes (Part-2)</h1>
                </div>
                {/*Posted Date*/}
                <div className="ml-4 mt-4 flex">
                    <p>
                        <span className="text-gray-400">Posted On </span>
                        <small className="hover:underline hover:text-cyan-500">May 22, 2021</small>
                    </p>
                    {/*Categories Belongs to*/}
                    <div className="ml-10">
                        <span className="inline-block bg-cyan-200 text-cyan-800 text-xs px-2 rounded-full uppercase font-semibold tracking-wide mr-2">
                            Perl
                        </span>
                        <span className="inline-block bg-cyan-200 text-cyan-800 text-xs px-2 rounded-full uppercase font-semibold tracking-wide mb-10">
                            Nextjs
                        </span>
                    </div>
                </div>
            </div>
        </div>
    )
}

export default BlogList
```

In the *BlogComponent.js* file, use the BlogList.js component. 

```javascript
import BlogList from "./BlogList";
function BlogComponent() {
    return (
        <div className="ml-4 mt-4 w-1/2 container mr-4">
            <BlogList />
        </div>
    )
}
export default BlogComponent
```

## Update index.js

In the last section, we need to use those components on our home page. Open index.js under the pages folder and replace the contents with:

```javascript
import Head from 'next/head'
import CategoriesComponent from "../components/Categories/CategoriesComponent";
import BlogComponent from "../components/Blog/BlogComponent";
export default function Home() {
  return (
    <div>
      <Head>
        <title>Ashutosh.dev - A place to learn Programming</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <div className="flex flex-auto">
          <CategoriesComponent />
          <BlogComponent />
          <div></div>
      </div>
    </div>
  )
}
```

Execute the following:

```bash
yarn dev
```


<img src="https://github.com/akuks/Misc/raw/main/Images/AshutoshDevHomePage.jpeg.png" />


As of now, anchor links are not working as we only create the basic layout of the app. We'll keep adding on the components as we progress in this series. 

Thanks for reading upto here. Stay tuned for the next article.

- The theme (cyan) of this blog is taken from TailwindCSS.com
- Excerpts of the storybook button and Header is from the storybook.com documentation.