DEV Community

William Gómez Ortega
William Gómez Ortega

Posted on • Originally published at Medium on

Creating a SSR site using Next JS and improving your development.

Setup a development with NextJS is basically to follow the main guides of https://nextjs.org/docs. However, setup it properly to have all the gadgets requires some experience struggling with it. This post will help you to leverage all this process.

Contents:

  1. Initial setup
  2. CSS and SCSS
  3. Basic Routing
  4. Static files, handle svgs
  5. Dynamic Routing
  6. Redux

1. Initial Setup:

To create a next project, we only need to create a folder and init a npm project:

mkdir best-ssr-ever
cd best-ssr-ever
npm init
Enter fullscreen mode Exit fullscreen mode

Install NextJS packages and dependencies:

npm install next react react-dom
Enter fullscreen mode Exit fullscreen mode

Add the following to package.json file:

Let’s test if everything is working, create a folder called pages :

mkdir pages
Enter fullscreen mode Exit fullscreen mode

Create a javascript file (the name of the file will be taken by NextJS as the route where the page will be set) and create a react component:

const Hello = () => <p>Hello NextJS!</p>;

export default Hello;
Enter fullscreen mode Exit fullscreen mode

Install all dependencies:

npm i
Enter fullscreen mode Exit fullscreen mode

Let’s run the dev server:

npm run dev
Enter fullscreen mode Exit fullscreen mode

In the address http://localhost:3000/hello we should watch our first page in NextJS. Let’s create a component and import it in our page.

Create a folder called components and inside another one called cta :

mkdir -p components/cta
Enter fullscreen mode Exit fullscreen mode

Create a file called index.js:

Let's import it in our hello page.

So far we are good, but let’s improve our code:

1.1. Config file.

If our project scales, there will be a moment when the relative imports will be like the following ‘ ../../../../package.js’ and will be difficult to track or modify something, that’s why we will start configuring the absolute import:

In NextJS there is a config file that we need to create next.config.js, which allows us to have a custom configuration. In this file, we can change how long will last the caching, how will be called our dist folder, set additional webpack configuration and so on. Let’s customize our webpack config in order to have absolute import:

First, create next.config.js in the root directory of our project:

module.exports = {}
Enter fullscreen mode Exit fullscreen mode

When we are working with webpack without NextJS, that is, we install webpack ourselves and create a webpack.config.js, to have a absolute import you must set the following:

In NextJS, the json exported in next.config.js can have a field called webpack that will allow us to override default webpack config.

module.exports = {
    webpack: (config, options) => {
        // Here modify anything you need about the config.
        return config
    }
}
Enter fullscreen mode Exit fullscreen mode

So, what we are going to do is to set the config in the following way:

Setting that, we can now modify the import we had on pages/hello.js

import Cta from 'components/cta'
Enter fullscreen mode Exit fullscreen mode

Run again npm run dev to see the changes in the browser.

Perfect, now you are much closer to have a perfect project setup.

Commit: https://github.com/williamegomezo/creating-ssr-next-js/commit/1f5c4896aa5567da15edc16f1e97cdcfa7fa77a1

1.2. Linters!

How did you bear a code without linters? Let’s install eslint to help us to set a standard linter configuration…

Run in the terminal:

npm install -g eslint

eslint --init
Enter fullscreen mode Exit fullscreen mode

Select:

1. ❯ To check syntax, find problems, and enforce code style
2. ❯ JavaScript modules (import/export)
3. ❯ Vue.js ... kidding ❯ React
4. Does your project use TypeScript? ❯ Obviously not!
5. ❯ Browser
6. ❯ Use a popular style guide
7. ❯ Airbnb (https://github.com/airbnb/javascript)
8. ❯ JavaScript
9. ❯ (missing dependencies) ... install them with npm? ❯ Y
Enter fullscreen mode Exit fullscreen mode

We will end with a .eslintrc.js file in our root path.

If we have in vscode the eslint extension you will notice that the IDE will be warning us with every lint error.

There are some additions we should include in our .eslintrc.js config, because we dont want to deal with them:

  • ‘React’ must be in scope when using JSXeslint(react/react-in-jsx-scope)
  • JSX not allowed in files with extension ‘.js’eslint(react/jsx-filename-extension)

So, let’s add to the file the following rules:

"rules": {    
    "react/react-in-jsx-scope": "off",
    "react/jsx-filename-extension": [1, { extensions: [".js", ".jsx"] }],
    "react/prop-types": [0],
    quotes: ["error", "double"],
    "arrow-parens": ["error", "as-needed"],
    "comma-dangle": ["error", "only-multiline"]
}
Enter fullscreen mode Exit fullscreen mode

But, we will still have to set some settings to avoid warnings about absolute import and other react features, first install babel-eslint :

npm install eslint@4.x babel-eslint@8 --save-dev
Enter fullscreen mode Exit fullscreen mode

Then add this to the .eslintrc.js file:

"settings": {
    "import/resolver": {
        "node": {
            "paths": ["./"]
        }
    },
    "react": {
        "pragma": "React",
        "version": "16.9.0"
    }
},
"parser": "babel-eslint",
Enter fullscreen mode Exit fullscreen mode

Almost ready, add in the globals the React field to avoid that eslint warns us about having React without importing, actually NextJS is importing it in background:

"globals": {
    "Atomics": 'readonly',
    "SharedArrayBuffer": 'readonly',
    "React": 'writable',
}
Enter fullscreen mode Exit fullscreen mode

Out final .eslintrc will look in this way:

If we run:

eslint .
Enter fullscreen mode Exit fullscreen mode

We will see only warnings of our lack of seniority.

Commit: https://github.com/williamegomezo/creating-ssr-next-js/commit/882cfa7f74ce85d0c613d1eb9c8160894dc4a5d6

2. CSS and SCSS configuration:

Well, first, NextJS in its documentation shows a Built-in CSS support, which uses styled-jsx to isolated scoped CSS. This is the example provided by them:

function HelloWorld() {
    return (
        <div>
            Hello world
            <p>scoped!</p>
            <style jsx>{`
                p {
                    color: blue;
                }
                div {
                    background: red;
                }
                @media (max-width: 600px) {
                    div {
                        background: blue;
                    }
                }
             `}</style>
             <style global jsx>{`
                 body {
                     background: black;
                 }
             `}</style>
         </div>
    )
}

export default HelloWorld;
Enter fullscreen mode Exit fullscreen mode

In this tutorial we are not going to use this, because I don’t like it :P and I prefer to have each file separated.

We will be using scss files, and for that we need a dependency: next-sass

We can find the package in the following link: https://github.com/zeit/next-plugins/tree/master/packages/next-sass

Its installation is really easy:

npm install --save [@zeit/next-sass](http://twitter.com/zeit/next-sass) node-sass
Enter fullscreen mode Exit fullscreen mode

To use it we only need to wrap the exported json of next.config.js :

const withSass = require('[@zeit/next-sass](http://twitter.com/zeit/next-sass)')

module.exports = withSass({
    /* config options here */
})
Enter fullscreen mode Exit fullscreen mode

I prefer to use an additional plugin called withPlugins, to start to add NextJS’s creators plugins. (https://github.com/cyrilwanner/next-compose-plugins)

npm install --save next-compose-plugins
Enter fullscreen mode Exit fullscreen mode

This is the template provided by them:

const withPlugins = require('next-compose-plugins');

const nextConfig = {
  distDir: 'build',
  webpack: (config, options) => {

    // modify the `config` here

    return config;
  },
};

module.exports = withPlugins([
  // add plugins here..
], nextConfig);
Enter fullscreen mode Exit fullscreen mode

Our config will look like this:

That is enough to style our cta:

Create in components/cta/ a file index.scss :

components/cta/index.scss

components/cta/index.js

Cool, now we can use scss. What if we use the cool CSS Modules?

Add field cssModules in next.config.js

module.exports = withSass({
    ...,
    cssModules: true
})
Enter fullscreen mode Exit fullscreen mode

This can be done in withPlugins, adding a second element in the array of plugins, which is the options of the plugin:

And change components/cta/index.js for:

css is a map, with the name of the class in the scss and the hash name generated for CSS modules. Example:

{
    superCta: "oz3A60u9ZTGJI-7A1n1go"
}
Enter fullscreen mode Exit fullscreen mode

In the browser we have:

<button class="oz3A60u9ZTGJI-7A1n1go">Click me</button>
Enter fullscreen mode Exit fullscreen mode

There is a problem we haven’t tackle, what if we want global styles, like resets, colors, media queries?

One solution could be to install a new dependency: sass-resources-loader

npm install sass-resources-loader
Enter fullscreen mode Exit fullscreen mode

And again, let’s add more rules to our next.config.js :

webpack: (config) => {
    ...

    config.module.rules.push({
        enforce: 'pre',
        test: /.scss$/,
        loader: 'sass-resources-loader',
        options: {
            resources: ['./styles/globals.scss'],
        },
    });

    return config;
}
Enter fullscreen mode Exit fullscreen mode

Basically we are adding a rule, where it will look in a folder called styles (that we are going to create in the root folder) and it will look for a file called globals.scss (set the name you want, it could be main.scss )

There in styles/globals.scss you can import any file you want to have as a global resource in every page:

But be cautious, this tool must be used to declare scss variables, all css styles added in these global file will be repeated in each component and this will increase the final css file size and will slow the browser.

A better solution is to override _app.js page. And set a custom App page that loads a global.scss. A basic example of _app.js can be found here:

https://nextjs.org/docs/advanced-features/custom-app

The first thing we need to understand is that next does not render a React App but miracle, it has by default a file called _app.js where the app is written, we can overload that document creating a file inside pages called _app.js

Create a folder called styles and place there a index.scss (or main.scss or globals.scss):

Import it in _app.js :

Commit: https://github.com/williamegomezo/creating-ssr-next-js/commit/b751a4c8c4c1d239aa4285f4a741cbc5b0b26616

3. Routing:

To make this more entertaining, let's create an app with purpose.

Let’s create the Tour of Heroes of our enemy! (https://moynokylxexp.angular.stackblitz.io/dashboard)

So, we have to create three pages:

  • /dashboard
  • /heroes
  • /detail/:id

We will let the third one for the step of dynamic routing

Create two js files in the folder pages : dashboard.js and heroes.js

pages/dashboard.js

pages/heroes.js

Check the routing visiting:

Well, now, how can we go from one page to another?

Let’s modify our Cta component to have the Link component of NextJS:

components/cta/index.js

We add Link from “next/link” which allows as to having a kind of SPA routing in a SSR app.

Let’s modify our pages:

pages/dashboard.js

pages/heroes.js

Our Cta looks ugly, I will add some styles to it.

.superCta {
    display: inline-block;
    background-color: white;
    color: black;
    font-family: 'Roboto';
    font-weight: 500;
    font-size: 20px;
    border: 1px solid;
    text-decoration: none;
    padding: 10px;
    margin: 10px;
} 
Enter fullscreen mode Exit fullscreen mode

Commit: https://github.com/williamegomezo/creating-ssr-next-js/commit/f58f5983556761aa94a5876b12fc3d2335b671a3

4. Static files:

Handling static files is easy, you must create only one folder: public, there you put any assets you want.

Let’s download an image: https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/React.svg/1200px-React.svg.png

And put in ./public/images/react.png

Remember our hello page?

pages/hello.js

There is also another way to handle static files and is importing then, and using libraries to load them:

SVGs: Dependencies everywhere: Let’s install svg-react-loader

npm install svg-react-loader

Again we have to modify our webpack config, adding the following rule:

config.module.rules.push(
    {
        test: /\.svg$/,
        exclude: /node_modules/,
        loader: 'svg-react-loader',
    },
);
Enter fullscreen mode Exit fullscreen mode

Go to material icons: https://material.io/resources/icons/?search=search&style=baseline

And look for search , save the icon in the folder /static/icons/.

Now, let’s load it. But first, let’s create another component, a search bar:

components/searchBar/index.js

function SearchBar(props) {
     const { placeholder } = props;

     return (
         <div>
         // Here there should be a search icon
         <input placeholder={placeholder} />
         </div>
     );
}

export default SearchBar;
Enter fullscreen mode Exit fullscreen mode

Now let’s add the svg:

import SearchIcon from 'public/icons/search.svg';
Enter fullscreen mode Exit fullscreen mode

And …

Adding it the to dashboard:

It would be better to wrap SearchIcon in a component maybe called SvgIcon and load dynamically the svg based on a prop. That will be done creating:

components/svgIcon/index.js

components/searchBar/index.js

I will add some styles to the searchBar:

components/searchBar/index.scss

Well, in order to have heroes, for now we will mock them:

public/mocks/heroes.json

Now, we know that the heroes will be on the route localhost:3000/mocks/heroes.json, obviously we can import them, but there is more fun if we fetch them, I will use the love of my live which is called axios, let's install it:

npm install axios
Enter fullscreen mode Exit fullscreen mode

Now, let’s use a functionality of NextJS that allow us to get an initial status of the page: getInitialProps

Our dashboard will look like:

pages/dashboad.js

For now, our search bar does not work, let’s wait to redux part to make it work.

But let’s create better a List component in order to have routing to the profile page of each hero.

components/List/index.js

As you can see we use our component Cta, so we can go to the profile of each Hero, to do that we need dynamic routing.

Commit: https://github.com/williamegomezo/creating-ssr-next-js/commit/8e29df076cb5330238939a4cbecaabaf0e445021

5. Dynamic routing:

Let’s create a folder inside pages called heroes and create a js file called [hero].js

pages/heroes/[hero].js

Note that we use a module of NextJS called useRouter, with this we can get an instance of the router and get the query parameters in the url, in this case the query parameter is what follows after heroes/ and Next looks if there is a js file in the folder heroes with parameters in its name ([hero].js, that is how hero becomes an argument int he query)

6. Redux:

I want to present you an example of how using redux and connect the components, to be honest this app is pretty simple to use Redux but this is for academic purposes.

Before starting let’s do it in the simple way, lets send a callback from the dashboard page to the searchBar in that way the can alter heroes array using the onChange of the searchBar

components/searchBar/index.js

Note that now SearchBar has a new prop, which is called filterUsingValue and this is just a function of the dashboard.js file that is sent as prop. Now we can execute that function a pass to is a value, the value of the input.

pages/dashboard.js

Note that we convert the page component into a React.component in order to use the state of the component and mutate it using this.setState with the function filterHeroes, note that this function is the one that is sent to the searchBar, so the search is in charge of call this function setting the value of the input.

Now let’s try the same but using Redux.

To use Redux, we need to do some tricky stuffs, sorry but everything has its cost.

Install the dependencies:

npm install redux react-redux next-redux-wrapper --save
Enter fullscreen mode Exit fullscreen mode

Let’s first create a folder called store, in this folder we will set two folders actions and reducers, in actions we will create the regular Redux actions types and actions:

store/actions/action-types.js

export const CHANGE_SEARCH = 'CHANGE_SEARCH';
Enter fullscreen mode Exit fullscreen mode

store/actions/index.js

In reducers we will create a Redux reducer to change the state:

store/reducers/searchReducer.js

Finally, we will create the store in store/index.js

Note that makeStore must be a function, because the wrapper we will use it require to create the store in this way.

Now, we have Redux as we would configure it in Create-React-App. Let’s modify _app.js in order to import the store and set the Provider.

pages/_app.js

As our pages are going to use getInitialProps we need to call it from _app.js getInitialProps.

Let’s add the dispatch in the SearchBar component to give it the ability of change the state.

components/searchBar/index.js

Note that we change from function component to React.Component to prettify a little bit the code and keep the component as a class. Using connect when we are exporting the component the dispatch function is set in the props of the component. That’s why in the constructor the dispatch is got from the props. Using dispatch and the action from the actions we can alter the state.

Now is time to refactor pages/dashboard.js

Note that now we dont need to pass a function to the searchBar component, we have decoupled it. Now the state of the reducer is in the property search and we can filter the list each time the props are updated in the render function.

Commit: https://github.com/williamegomezo/creating-ssr-next-js/commit/eea82b221537b358b7dba27603bc71598f3f9571

Top comments (0)