There are several reasons you would want to incorporate Bootstrap into your Next.js application, one of which is that Bootstrap comes prebuilt with a ton of utility classes and components that make life easier.
However, when integrating bootstrap into a Next.js application (as opposed to a standard React app), certain errors occur, particularly when using Bootstrap JavaScript features such as toggle navbars and modals. And these issues are frequently caused by Next.js’s SSR functionality.
Why is Next.js SSR a problem for Bootstrap?
Server-side-rendering (SSR) is a functionality of Next.js and other JavaScript libraries/frameworks that allow web applications to convert HTML files on the server into fully rendered HTML pages for the client. This means that all activities are performed on the server, and markup files generated as a result of these processes are rendered to the client.
The included JavaScript in Bootstrap, on the other hand, requires the browser’s document
object to function. And, because Next.js is SSR by default, this implies that the document
object is not ready until the page is fully loaded, which is why you would get the following error when attempting to use Bootstrap’s JavaScript capabilities in a Next.js application:
This article will explain how to fix this error as well as how to effectively use the full capabilities of Bootstrap in a Next.js application.
Adding Bootstrap to Next.js
There are several approaches to incorporating Bootstrap into a Next.js application. However, the most common is to install the Bootstrap package. Before we get started, let’s create a new Next.js app:
npx create-next-app my-app
Installing the Bootstrap module
Once the project has been created, we can easily add the most recent stable version of Bootstrap to it by running the following command:
npm install bootstrap
Following the installation of Bootstrap, we can import the minified Bootstrap CSS file into the Next.js entry pages/_app.js
file, as shown below:
import "bootstrap/dist/css/bootstrap.min.css"; // Import bootstrap CSS import "../styles/globals.css"; function MyApp({ Component, pageProps }) { return <Component {...pageProps} />; } export default MyApp;
Like in the code above, you want to make sure that you’ve imported Bootstrap before your custom CSS file so that it would be easier to override Bootstrap’s default styling with this file (if the need arises, of course).
Using JavaScript features
As previously explained, if we directly import the Bootstrap-bundled JavaScript file, we will get a 'document is not defined'
error. We may, however, leverage React’s useEffect()
Hook to accomplish the import:
// src/_app.js import { useEffect } from "react"; useEffect(() => { require("bootstrap/dist/js/bootstrap.bundle.min.js"); }, []);
The useEffect()
Hook in React is used to instruct our React components that they need to do something after rendering, and in this scenario, we’d use it to import the bundled Bootstrap JavaScript file.
With this addition, the complete code for our _app.js
file would look like this:
import "bootstrap/dist/css/bootstrap.min.css"; import "../styles/globals.css"; import { useEffect } from "react"; function MyApp({ Component, pageProps }) { useEffect(() => { require("bootstrap/dist/js/bootstrap.bundle.min.js"); }, []); return <Component {...pageProps} />; } export default MyApp;
Let’s create a simple modal to try things out. Open the default pages/index.js
file and replace its content with the following code:
export default function Home() { return ( <div className="d-flex justify-content-center align-items-center"> <button type="button" className="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" > Launch demo modal </button> <div className="modal fade" id="exampleModal" tabIndex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" > <div className="modal-dialog"> <div className="modal-content"> <div className="modal-header"> <h5 className="modal-title" id="exampleModalLabel"> Modal title </h5> <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" ></button> </div> <div className="modal-body">...</div> </div> </div> </div> </div> ); }
If we run our app with (npm run dev
) and preview its output in the browser, we should observe that everything works as intended:
Programmatically invoking Bootstrap components
There are scenarios where you’d want to trigger Bootstrap components programmatically rather than by adding the data
property to a button as we did in our previous example.
For modals, for example, the Bootstrap package exports a module that allows us to perform this operation:
import bootstrap from "bootstrap"; const showModal = () => { const myModals = new bootstrap.Modal("#exampleModal"); myModal.show(); };
If we run this code, however, we’ll still get the 'document is not defined'
error because we’d clearly imported Bootstrap into this current page also.
One approach to fixing this is to simply use JavaScript destructuring syntax to import the aforementioned module in our custom function, as shown in the code below:
const showModal = () => { const { Modal } = require("bootstrap"); const myModal = new Modal("#exampleModal"); myModal.show(); };
We can then call the showModal()
function to easily display our modal:
<button type="button" className="btn btn-primary" onClick={showModal}> Launch demo modal </button>
Applying this to a sample page, our full code would look like this:
// pages/index.js export default function Home() { const showModal = () => { const { Modal } = require("bootstrap"); const myModal = new Modal("#exampleModal"); myModal.show(); }; return ( <div className="d-flex"> <button type="button" className="btn" onClick={showModal}> Launch demo modal </button> <div className="modal fade" id="exampleModal" tabIndex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" > <div className="modal-dialog"> <div className="modal-content"> <div className="modal-header"> <h5 className="modal-title" id="exampleModalLabel"> Modal title </h5> <button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close" ></button> </div> <div className="modal-body"> . . . </div> </div> </div> </div> </div> ); }
This solution works nicely with all Bootstrap components that support toggle via JavaScript. Below is an example of how it might be used in a carousel:
// pages/index.js export default function Home() { const toggleCarousel = (action) => { const { Carousel } = require("bootstrap"); const carousel = new Carousel("#myCarousel"); if (action === "next") { carousel.next(); } else { carousel.prev(); } }; return ( <> {" "} <div> <button className="btn btn-primary" onClick={() => toggleCarousel("prev")} > Prev </button> <button className="btn btn-primary ms-3" onClick={() => toggleCarousel("next")} > Next </button> </div> <div> <div id="myCarousel" className="carousel slide" data-bs-touch="false" data-bs-interval="false" style= > <div className="carousel-inner"> <div className="carousel-item active"> <img src="https://picsum.photos/id/237/700/700" /> </div> <div className="carousel-item"> <img src="https://picsum.photos/id/123/700/700" /> </div> <div className="carousel-item"> <img src="https://picsum.photos/id/234/700/700" /> </div> </div> </div> </div> </> ); }
Running the code above, we get the following output in our browser:
You can alternatively create these Bootstrap components as custom React components and then import them into your pages using Next.js’s dynamic import feature while disabling SSR.
With the Next.js dynamic import feature, we are able to import components dynamically and work with them. While the dynamic import allows server-side rendering, we can disable it if desired.
Below is how to import a sample component in this manner:
import dynamic from 'next/dynamic' const DynamicComponentNoSSR = dynamic( () => import('../components/SampleComponent'), { ssr: false } )
And in our component file located at ../components/SampleComponent
, we are able to run any client-side JavaScript-related code before the component is rendered.
To try things out, let’s create a new file, Toast.js
, in the root source of our Next.js project, and paste the following content into it:
const bootstrap = require("bootstrap"); const Toast = () => { const showToast = () => { const toast = new bootstrap.Toast("#liveToast"); toast.show(); }; return ( <div> <button type="button" onClick={showToast} className="btn"> Show Toast </button> <div className="toast-container position-fixed p-3 top-0"> <div id="liveToast" className="toast" role="alert" aria-live="assertive" aria-atomic="true" > <div className="toast-header"> <img src="..." className="rounded" alt="..." /> <strong className="me-auto">Bootstrap</strong> <small>2 secs ago</small> <button type="button" className="btn-close" data-bs-dismiss="toast" aria-label="Close" ></button> </div> <div className="toast-body"> Hello, world! This is a toast message. </div> </div> </div> </div> ); }; export default Toast;
The code above is simply a template for a Bootstrap Toast
component that we are triggering programmatically with a custom function, and as you can see, we have also imported the Bootstrap module at the top level of this component.
Now, let’s go to our public/index.js
file and dynamically import this component while disabling SSR:
import dynamic from "next/dynamic"; const Toast = dynamic(() => import("../Toast"), { ssr: false, }); export default function Home() { return ( <> <Toast /> </> ); }
If we run our code and click the button, we should see that everything works perfectly without throwing any errors:
Conclusion
Throughout this article, we’ve discussed how to use Bootstrap’s full capabilities in a Next.js application. We also analyzed the most common issue that occurs while attempting to use Bootstrap JavaScript features, as well as the many methods for resolving this error. I hope this answers your questions about integrating Bootstrap with Next.js!
The post Handling Bootstrap integration with Next.js appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/AvIejwO
via Read more