TutorialsLast Updated May 7, 20265 min read

Continuous integration for Next.js applications

Fikayo Adepoju

Fullstack Developer and Tech Author

Developers use Next.js to create production-grade applications. Features like routing, code-splitting, bundling, TypeScript, and built-in CSS support are provided out-of-the-box by Next.js. For many developers, this ease of use is what makes Next.js their preferred React.js framework for production.

In this tutorial, I’ll show you how to set up continuous integration for your Next.js applications so that you can add features with confidence, knowing that automated tests will prevent any bugs from reaching production.

After you’ve completed this tutorial, be sure to check out the companion piece on continuous deployment for Next.js projects.

Prerequisites

To follow this tutorial, you will need:

  1. Basic knowledge of Javascript
  2. Node.js 22 (active LTS) or newer installed on your system
  3. A CircleCI account
  4. A GitHub account

With all of these installed and set up, you can begin the tutorial.

Creating a new Next.js project

To begin, create a new Next.js project by running this command:

npx create-next-app@latest next-app-testing

The CLI opens with one prompt asking whether to use the recommended Next.js defaults (TypeScript, ESLint, Tailwind CSS, App Router). Picking No, customize settings steps through each option individually. This tutorial follows the JavaScript path: no TypeScript, no Tailwind, App Router, no src/ directory, default @/* import alias.

To skip the prompts entirely and use those answers, run the command with explicit flags instead:

npx create-next-app@latest next-app-testing \
  --js --eslint --no-tailwind --app --no-src-dir \
  --import-alias "@/*" --use-npm

This creates a Next.js application inside a next-app-testing folder (you can give the folder any name you choose). Once the scaffolding process is done, go into the root of the project and run the application:

cd next-app-testing
npm run dev

This will boot up a development server that serves the application at http://localhost:3000. Load this URL in your browser.

New app (local) - Next.js

Installing and setting up Jest for testing

Your next step is to set up the testing framework and the utilities needed to run tests. You will be using Jest as a testing framework along with React Testing Library. These are the packages you will be installing:

  • jest: The testing framework
  • jest-environment-jsdom: A browser-like DOM for tests
  • @testing-library/react: Utilities for rendering React components in tests
  • @testing-library/dom: Required peer dependency of @testing-library/react since v16
  • @testing-library/jest-dom: Custom Jest matchers for asserting on the DOM

Install these packages with one command:

npm install -D jest jest-environment-jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom

Next.js ships a Jest transformer at next/jest.js that wires Jest into the Next.js compiler. It handles JSX/TSX, mocks CSS modules and image imports, loads .env files, and applies any SWC config from next.config.js. Create jest.config.js at the root of your project:

const nextJest = require("next/jest.js");

const createJestConfig = nextJest({
  // Path to your Next.js app, used to load next.config.js and .env files
  dir: "./",
});

/** @type {import('jest').Config} */
const config = {
  clearMocks: true,
  testEnvironment: "jsdom",
  setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
};

module.exports = createJestConfig(config);

Create jest.setup.js next to it. This file runs once before each test suite and pulls in the custom matchers from @testing-library/jest-dom:

import "@testing-library/jest-dom";

Finally, add a test script to package.json:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "test": "jest"
  }
}

Rendering React.js components

Now you can begin writing tests. The project contains everything it needs to run them.

Create a __tests__ folder. This is a special folder where jest looks for test files at the root of the project. Add a test file named home.test.jsx. Enter this code in the file:

import { render, screen } from "@testing-library/react";
import Home from "../app/page";

describe("Home Page", () => {
  it("renders the get-started heading", () => {
    render(<Home />);

    const heading = screen.getByRole("heading", { level: 1 });

    expect(heading).toBeInTheDocument();
    expect(heading).toHaveTextContent(/edit the page\.js file/i);
  });
});

The render function from React Testing Library mounts the Home component (imported from app/page.js) into a virtual DOM. The test then queries the rendered output for an <h1> and asserts both that it’s in the document and that its text matches the default landing page heading.

Querying by role rather than literal copy keeps the test stable when Vercel rewords the default create-next-app page.

Run this test:

npm run test

Tests local run 1

Automating tests for continuous integration

Your tests are running, and things are good. But you can make things even better by using continuous integration to automate running your tests. With automation, your tests will run every time you push updates to your repository.

Begin by creating a folder named .circleci at the root of the project. Add a configuration file named config.yml to it. In this file, enter this code:

version: 2.1
orbs:
  node: circleci/node@7.2.1
jobs:
  build-and-test:
    docker:
      - image: cimg/node:lts
    steps:
      - checkout
      - node/install-packages
      - run:
          command: npm run test
workflows:
  build-and-test:
    jobs:
      - build-and-test

The cimg/node:lts executor already has Node installed, so the job goes straight to node/install-packages (which runs npm ci and caches node_modules for subsequent builds) and then to npm run test.

Review pushing your project to GitHub to push your project to GitHub.

Next, log into your CircleCI account. If you signed up with your GitHub account, all your repositories will be displayed on your project’s dashboard.

Next to your next-app-testing project, click Set Up Project.

Enter the name of the branch your configuration file is on and click Set Up Project.

This triggers the pipeline and builds successfully.

Build success - CircleCI

Good work!

Conclusion: Reducing the burden of boilerplate code

Next.js is an impressive framework for building production applications, in large part because it provides features that reduce the burden of boilerplate code. You don’t want to find broken code once your app is in production. In this tutorial, you learned how to test your features and ensure that the tests run automatically every time you push new code. This goes a long way in making sure that defective code is not pushed to any deployment environment.

With this knowledge, you’re now ready to set up a continuous deployment pipeline so that your well-tested updates are automatically deployed to your production environment. You can learn how to do that in Continuous deployment for Next.js applications.

You can access the full sample project from this tutorial on GitHub.

Happy coding!


Fikayo Adepoju is a LinkedIn Learning (Lynda.com) Author, Full-stack developer, technical writer, and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0, and The New Stack blogs, and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.