NextJS, Bootstrap 5 : an in-depth tutorial
Tags: nextjs, bootstrap, beginners
Published: 2021-12-06 09:29:24

Article originally published here : https://bootiful.org/blog/nextjs-bootstrap

Today we'll see how to install, customize, and use dynamic component of Bootstrap 5 with NextJS.

First, let's see how to use Bootstrap 5 with NextJS, without using any 3rd party library (other than Bootstrap itself, of course). At the end of the article, we will see other way to do it.

## Prerequisite : node, yarn

For this tutorial, NodeJS is required, and you can use yarn or npm as you wish, but yarn will be used here.

```bash
$> node --version
v14.15.1
$> yarn --version
1.22.10
```
It should not be a problem if you have slightly different versions.

## Create a fresh new NextJS app

Some kind of magic now : the creation of a default NextJS app is embedded into the yarn executable

```bash
$> yarn create next-app next-raw-bootstrap
```

Press Enter, a new app is magically building files, directory, and first few dependencies for you.

Notice that `next-raw-bootstrap` is the name of your new app - I used "raw" here to emphasize that we will use Bootstrap only.

Now go to the "next-raw-bootstrap" directory and open your favorite code editor at the root of "next-raw-bootstrap". 

```bash
$> cd next-raw-bootstrap
```

You can have a look to the directory structure and the created files, particularly package.json : very few dependencies, but already everything needed to lint and hot-reload code locally. Nice !

```bash
$/next-raw-bootstrap> yarn dev
yarn run v1.22.10
$ next dev
**ready** - started server on http://localhost:3000
```

Now open the browser at the indicated URL (here http://localhost:3000)

<figure>
  <img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%" src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638391044/bootstrap/next2Z.png" loading="lazy" alt="NextJS default web page">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">NextJS default web page</figcaption>

NextJS default website should now appear in your browser. There is a few CSS styles, and a default index page (what you can see by default is under `pages/index.js`)



## Bootstrap 5 HTML : a not too easy sample

Now go to `pages/index.js`, delete all existing content, and replace by this one :

```jsx
import Head from 'next/head'

export default function Home() {

  return (
    <div>
      <Head>
        <title>Next and Bootstrap</title>
        <meta name="description" content="A demo about NextJS and Bootstrap 5" />
      </Head>

      <div className="toast align-items-center text-white bg-primary border-0 position-absolute top-50" role="alert" aria-live="assertive" aria-atomic="true">
        <div className="d-flex">
          <div className="toast-body">
            Hello, world! This is a toast message.
          </div>
          <button type="button" className="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
        </div>
      </div>

      <div className="collapse" id="navbarToggleExternalContent">
        <div className="bg-dark p-4">
          <h5 className="text-white h4">Collapsed content</h5>
          <span className="text-muted">Toggleable via the navbar brand.</span>
        </div>
      </div>
      <nav className="navbar navbar-dark bg-dark">
        <div className="container-fluid">
          <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggleExternalContent" aria-controls="navbarToggleExternalContent" aria-expanded="false" aria-label="Toggle navigation">
            <span className="navbar-toggler-icon"></span>
          </button>
        </div>
      </nav>

      <main>
        <section className="py-5 text-center container">
          <div className="row py-lg-5">
            <div className="col-lg-6 col-md-8 mx-auto">
              <h1 className="fw-light">Bootstrap 5.1.3 example</h1>
              <p className="lead text-muted">Short text.</p>
            </div>
          </div>
        </section>
      </main>

    </div>
  )
}
```

The example is quite simple, but also complex enough to show how dynamic component should work.

Here is what you should see in the browser :

<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:40%" src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638375256/bootstrap/next1.png" loading="lazy" alt="Unstyled bootstrap content">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:40%">Unstyled bootstrap content</figcaption>



## Add SaSS to allow Bootstrap customisation

CSS alone might not be enough for a production-ready project. SaSS allow to define variables, functions, and a bunch of features above CSS to allow you a long-term, maintainable CSS design system.

And good news : Bootstrap source files are based on SaSS.

Stop the local server. And type :

```bash
$/next-raw-bootstrap> yarn add -D sass@1.43.5
```

**Side note** The "-D" option here means that sass will be used in development mode only. Some dependencies are needed only during development time, like linting, other only on build time, like building CSS with SaSS. All dependencies that will **not** end up in the browser can be safely put inside the development section of your package.json file.


## Get Bootstrap and NextJS working together (styling only)

Now that SaSS is installed, let's install bootstrap and it's required JS dependency PopperJS :

```bash
$/next-raw-bootstrap> yarn add @popperjs/core@2.11.0
$/next-raw-bootstrap> yarn add bootstrap@5.1.3
```


Now check your package.json file

```json
{
  "name": "next-raw-bootstrap",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@popperjs/core": "2.11.0",
    "bootstrap": "5.1.3",
    "next": "12.0.4",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "eslint": "7",
    "eslint-config-next": "12.0.4",
    "sass": "1.43.5"
  }
}
```

You should now see "popper" and "bootstrap" inside the dependencies, and "sass" inside the devDependencies.

Now add an application.scss file (scss in the extension for SaSS files) here : styles/application.scss

```bash
$/next-raw-bootstrap> touch styles/applications.scss
```
And import Bootstrap as follow
```scss
// inside styles/application.scss
@import 'bootstrap/scss/bootstrap.scss';
```
And import this application.scss into your _app (you can safely remove other styles) :
```jsx
// inside pages/_app.js
import '../styles/application.scss'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp
```

Relaunch your local server :

```bash
$/next-raw-bootstrap> yarn dev
```

and open your browser at localhost:3000

<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%" src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638375256/bootstrap/next7.png" loading="lazy" alt="Bootstrap, style only">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">Bootstrap, style only</figcaption>

Ok ! The application uses now Bootstrap 5 to style the application, but 

 - The navigation button (hamburger menu) does not work
 - The Toast is not showing (as if it's in the HTML). Bootstrap do not display Toast messages by default, you have to trigger it manually. (see https://getbootstrap.com/docs/5.1/components/toasts/)

## JavaScript of Bootstrap within NextJS

Contrary to Tailwind, Bootstrap is not only a CSS framework and a design system. It comes also with a bunch of dynamic components like collapsible navigation bar (also called "navbar"), toasts messages, and so on. CSS is not really able to bring this "dynamic" part. But this is precisely why Bootstrap sometimes needs JavaScript to get *every feature* working properly. 

Some component like the collapsible navbar doesn't need anything else but loading the whole bootstrap JS library.

Let's change the top part of pages/index.js (returned HTML stay the same)

```js
// pages/index.js, returned HTML is unchanged
import React, { useEffect } from "react";
import Head from 'next/head'

export default function Home() {

  // See https://en.reactjs.org/docs/hooks-effect.html
  useEffect(() => {

      // Run code on client-side only : ensure document is here
      if (typeof document !== undefined) {

        // load JS bootstrap dependency
        require('bootstrap/dist/js/bootstrap')   

      }
  // Run useEffect only once
  // Read https://css-tricks.com/run-useeffect-only-once/
  }, [])

  return (
    // same HTML as before
  )

```

We use here a hook (callback inside useEffect) to modify the DOM once it has properly loaded. We check that the document exists (which means that we are working client-side, inside the browser, and not server side), and once all conditions are met, the  Bootstrap's JS lib is loaded from node_modules.

Reload the page in your browser.


<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%"  src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638375256/bootstrap/next8.png" loading="lazy" alt="Bootstrap, collapsible navbar">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">Bootstrap, collapsible navbar</figcaption>


Great ! If you click on the hamburger menu, you will now see that the component works properly.

But... the toast message is still not displayed.

## Bootstrap JavaScript-based initialization within NextJS

So we have now some of the Bootstrap JavaScript components that works. The ones that doesn't require any special initialization. However, some components like toast messages (https://getbootstrap.com/docs/5.1/components/toasts/) need to be manually triggered. Let's see how.


```js
// pages/index.js, returned HTML is unchanged
import React, { useEffect } from "react";
import Head from 'next/head'

export default function Home() {

  // See https://en.reactjs.org/docs/hooks-effect.html
  useEffect(() => {

      // Run code on client-side only : ensure document is here
      if (typeof document !== undefined) {

        // load JS bootstrap dependency
        let bootstrap = require('bootstrap/dist/js/bootstrap')

        // find all toasts
        let toastElList = [].slice.call(document.querySelectorAll('.toast'))
        let toastList = toastElList.map(function (toastEl) {
          return new bootstrap.Toast(toastEl)
        })

        // show each toast explicitly
        toastList.forEach( function(element, index) {
          element.show()
        })
      }
  // Run useEffect only once
  // Read https://css-tricks.com/run-useeffect-only-once/
  }, [])

  return (
    // same HTML as before
  )
```

Let's check your local server is running, if not run

```bash
$/next-raw-bootstrap> yarn dev
```
And reload your browser :

<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%"  src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638382731/bootstrap/next9.png" loading="lazy" alt="Bootstrap, toast message">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">Bootstrap, toast message</figcaption>



Ta-da ! Now the Toast message is displayed. 

## Customize Bootstrap style for a NextJS app

Ok, now both CSS and JS work for Bootstrap inside a NextJS app, let's see how to customize Bootstrap, in order to maintain a coherent 

```scss
// inside styles/application.scss
$lead-font-size: 5rem;
$lead-font-weight: 800;
@import 'bootstrap/scss/bootstrap.scss';
```

Let's see how the app is displayed now :

<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%"  src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638375257/bootstrap/next3.png" loading="lazy" alt="Bootstrap, bold message">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">Bootstrap, custom lead font</figcaption>


As you can see, Bootstrap ".lead" class has been changed, just by changing SaSS variables. In Bootstrap 5 there are a lot of variables. All the variable you can set are available here :

https://github.com/twbs/bootstrap/blob/v5.1.3/scss/_variables.scss

## Show me the code, please

Final code source for this first part can be seen here : https://github.com/bdavidxyz/next-raw-bootstrap

## Restart from scratch, with Bootstrap 5 components

Why would you restart from scratch ? Let's admit it : use Bootstrap 5 "as is" inside a NextJS is not very elegant. Because ReactJS is about *components*. Wouldn't it be nice if the Bootstrap components were already wrapped somewhere into React components ? 

Good news : it has already been made here :
https://github.com/react-bootstrap/react-bootstrap

**Side note** There are two similar projects : react-bootstrap and reactstrap. They slightly differ in the way components are coded, but are very similar when you use them. So pick your favorite. I personally find react-bootstrap more complete, so for this tutorial we will use react-bootstrap.

## (Re)create a fresh new NextJS app

```js
$> yarn create next-app next-bootstrap
$> cd next-bootstrap
$/next-bootstrap> yarn add -D sass@1.43.5
$/next-bootstrap> yarn add @popperjs/core@2.11.0
$/next-bootstrap> yarn add  bootstrap@5.1.3
$/next-bootstrap> echo "@import 'bootstrap/scss/bootstrap.scss';" > styles/application.scss
```
Then make sure that pages/_app is importing application.scss  :

```jsx
// pages/_app.js
import '../styles/application.scss'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp
```

So far, nothing new.

One last thing : modify pages/index.js as follow : 

```jsx
import Head from 'next/head'

export default function Home() {
  return (
    <div>
      <Head>
        <title>NextJS app with react-bootstrap</title>
      </Head>

      <main>
        <h1>
          Welcome
        </h1>
      </main>
    </div>
  )
}
```

Run your local server with `yarn dev` and let's see how the default page is styled :

<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%"  src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638375256/bootstrap/next5.png" loading="lazy" alt="Bootstrap, simple message">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">Bootstrap, simple message</figcaption>


Ok ! Now it's type to see the magic of React components

## React-based Bootstrap components thanks to react-bootstrap

```js
$/next-bootstrap> yarn add  react-bootstrap@2.0.3
```

And change `pages/index.js` as follow :

```jsx
import Head from 'next/head'
import {Accordion} from 'react-bootstrap'

export default function Home() {
  return (
    <div>
      <Head>
        <title>NextJS app with react-bootstrap</title>
      </Head>

      <main>
        <h1>
          Welcome
        </h1>

        <Accordion defaultActiveKey="0">
          <Accordion.Item eventKey="0">
            <Accordion.Header>Accordion Item #1</Accordion.Header>
            <Accordion.Body>
              Lorem ipsum dolor sit amet...
            </Accordion.Body>
          </Accordion.Item>
          <Accordion.Item eventKey="1">
            <Accordion.Header>Accordion Item #2</Accordion.Header>
            <Accordion.Body>
              Duis aute irure dolor in reprehenderit in voluptate...
            </Accordion.Body>
          </Accordion.Item>
        </Accordion>

      </main>
    </div>
  )
}
```

<figure>
<img style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%"  src="https://res.cloudinary.com/bdavidxyz-com/image/upload/v1638375256/bootstrap/next6.png" loading="lazy" alt="Bootstrap, accordion component">
</figure>
<figcaption style="display:block;float:none;margin-left:auto;margin-right:auto;width:60%">Bootstrap, accordion component</figcaption>


click on the accordion to see how the UX flows...

Nice ! Instead of manually pull the JS Bootstrap lib inside a useEffect hook, we simply imported here the component, this is a lot more intuitive.

## Code for this last example

https://github.com/bdavidxyz/next-bootstrap

## Credits

NextJS documentation : https://nextjs.org/docs

Bootstrap documentation : https://getbootstrap.com/docs/5.1/

React-Bootstrap repository : https://github.com/react-bootstrap/react-bootstrap