What We're Building
We'll create a complete CI/CD pipeline for a React application using GitLab CI/CD. This pipeline will automatically run tests, build the application, and deploy it to a static hosting service on every code push. You'll have a production-ready deployment workflow by the end.
Prerequisites: A GitLab account, Node.js 18+ installed, and a basic React project. We'll use GitLab Pages for deployment, but the pattern works for any static host.
Project Setup
First, ensure you have a React project ready. If you don't have one, create it with Create React App. We'll also initialize a Git repository and connect it to GitLab.
# Create a new React app if you don't have one
npx create-react-app my-react-cicd
cd my-react-cicd
# Initialize Git and connect to your GitLab remote
git init
git add .
git commit -m "Initial commit"
# Create a new project on GitLab.com, then link it
git remote add origin https://gitlab.com/your-username/my-react-cicd.git
git push -u origin main
Verify your project structure includes these key files: package.json, src/ directory, and public/ directory. Your app should run locally with npm start.
Configuring the GitLab CI/CD File
The core of our pipeline is the .gitlab-ci.yml file in the project root. This YAML file defines the jobs, stages, and scripts GitLab Runner will execute.
# .gitlab-ci.yml
image: node:20-alpine
stages:
- test
- build
- deploy
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
before_script:
- npm ci --cache .npm --prefer-offline
test:
stage: test
script:
- npm test -- --watchAll=false --passWithNoTests
build:
stage: build
script:
- npm run build
artifacts:
paths:
- build/
expire_in: 1 hour
pages:
stage: deploy
script:
- cp -r build/* public/
artifacts:
paths:
- public
only:
- main
This configuration defines three stages that run sequentially. The node:20-alpine image provides a lightweight Node.js environment. Caching node_modules speeds up subsequent pipeline runs.
⚠️ Note: Thenpm cicommand (used inbefore_script) requires apackage-lock.jsonfile. Ensure it exists in your repository. It provides reliable, reproducible installs compared tonpm installin CI.
Adding Tests and Build Verification
Our test stage runs Jest, which comes with Create React App. Let's add a simple test to verify our pipeline executes tests correctly. We'll create a component and its test.
// src/components/Status.js
import React from 'react';
function Status({ isActive }) {
return (
<div data-testid="status">
Status: {isActive ? 'Active' : 'Inactive'}
</div>
);
}
export default Status;
// src/components/Status.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import Status from './Status';
test('displays Active status correctly', () => {
render(<Status isActive={true} />);
expect(screen.getByTestId('status')).toHaveTextContent('Status: Active');
});
test('displays Inactive status correctly', () => {
render(<Status isActive={false} />);
expect(screen.getByTestId('status')).toHaveTextContent('Status: Inactive');
});
Commit these files and push to trigger the pipeline. In your GitLab project, navigate to CI/CD → Pipelines. You should see a running pipeline with three jobs: test, build, and pages.
The test job output will show Jest executing both tests. The build job creates the production bundle in the build/ directory, saved as an artifact. The pages job copies this to public/ for GitLab Pages.
Configuring GitLab Pages Deployment
GitLab Pages hosts static sites from the public/ directory. After the pipeline completes, we need to adjust our React app to work with the Pages subpath. Update package.json with a homepage field.
// package.json
{
"name": "my-react-cicd",
"version": "0.1.0",
"private": true,
"homepage": "https://your-username.gitlab.io/my-react-cicd",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
}
Replace your-username with your GitLab username. This ensures React builds assets with correct absolute paths. Push this change to trigger another pipeline run.
⚠️ Note: The GitLab Pages URL follows the pattern https://username.gitlab.io/project-name. If you use a custom domain, set the homepage to your domain instead.
Enhancing the Pipeline with Quality Checks
Let's add a linting stage to catch code quality issues early. We'll run ESLint (included with Create React App) before tests. Update the YAML file with a new stage and job.
# Updated .gitlab-ci.yml
image: node:20-alpine
stages:
- lint
- test
- build
- deploy
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
before_script:
- npm ci --cache .npm --prefer-offline
lint:
stage: lint
script:
- npm run lint
test:
stage: test
script:
- npm test -- --watchAll=false --passWithNoTests
build:
stage: build
script:
- npm run build
artifacts:
paths:
- build/
expire_in: 1 hour
pages:
stage: deploy
script:
- cp -r build/* public/
artifacts:
paths:
- public
only:
- main
The lint job runs npm run lint, which executes ESLint with the default React configuration. If any linting errors exist, the job fails and stops the pipeline, preventing problematic code from advancing.
Push this updated configuration. Your pipeline now has four stages. The lint job completes quickly, providing immediate feedback on code style violations.
Testing and Verification
Verify the entire pipeline works end-to-end. Make a small change to your application, commit, and push to GitLab.
# Make a change, for example update App.js
git add .
git commit -m "Update homepage text"
git push origin main
Go to your GitLab project → CI/CD → Pipelines. Watch the pipeline progress through lint, test, build, and deploy stages. All jobs should show green checkmarks.
Once the pages job completes, navigate to Settings → Pages in your GitLab project. You'll see the URL where your React app is deployed. Click it to see your live application.
Test the deployment thoroughly. Check that all routes work (if using React Router) and assets load correctly. The site should be identical to your local production build.
⚠️ Note: GitLab Pages may take 1-5 minutes to update after the pipeline finishes. If you see a 404, wait a few minutes and refresh. Clear your browser cache if you see an old version.
Final Pipeline and Next Steps
You now have a fully automated CI/CD pipeline for your React application. Every push to the main branch triggers linting, testing, building, and deployment. The pipeline catches errors early and ensures only working code reaches production.
You can extend this pipeline by adding integration tests, security scanning, or deployment to other platforms like AWS S3 or Netlify. The GitLab CI/CD pattern remains the same—define jobs in stages, use artifacts to pass files between jobs, and control deployments with branch rules.