Skip to main content
Cypress App

Component Testing Configuration

info
What you'll learn​
  • How Cypress uses a dev server and bundler to run component tests
  • How to configure the recommended devServer object syntax
  • How Cypress detects and merges your Vite or Webpack configuration
  • How to use a custom index file or spec pattern
  • How to implement a fully custom dev server for unsupported bundlers

When you launch Cypress for the first time in a project, the app will automatically guide you through setup and configuration. The Launchpad detects your UI framework and bundler, lists the required dependencies needed for you to install, and scaffolds a cypress.config file with a component.devServer block.

The sections below explain what that configuration does and how to customize it. For framework-specific options (such as Angular monorepos or Next.js), also see the overview guide for your UI library:

Dev Server and Bundler​

Component tests run inside a real browser, but unlike end-to-end tests they do not visit your production or staging app. Instead, Cypress starts a dev server that compiles and serves your component specs on demand.

The dev server is responsible for:

  1. Compiling each spec file (and your support file) with the same transforms your app uses in development (JSX/TSX, Vue SFCs, CSS modules, path aliases, and so on).
  2. Serving those compiled files over HTTP so Cypress App can load them.
  3. Shutting down cleanly when you close the Cypress App or finish a test run.

Cypress ships with built-in dev server implementations for Vite and Webpack. You do not need to install @cypress/vite-dev-server or @cypress/webpack-dev-server separately, they are bundled with the Cypress App.

How it works at runtime​

When you open or run component tests, Cypress:

  1. Reads component.devServer from your Cypress config file.
  2. Starts the matching dev server (Vite or Webpack) on an available port.
  3. Sets baseUrl to http://localhost:<port>.
  4. Loads your component index HTML (by default cypress/support/component-index.html) and dynamically imports your support file (if configured) and the active spec, then hands control to Cypress.

The recommended way to configure component testing is to use the component.devServer object. Specify your UI framework and bundler and Cypress will wire up the correct dev server implementation for you:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
devServer: {
framework: 'react', // your UI framework
bundler: 'vite', // 'vite' or 'webpack'
},
},
})

This is the configuration that Cypress App scaffolds during project setup. It is all most projects need.

Supported framework and bundler values​

frameworkSupported bundler valuesNotes
reactvite, webpack
vuevite, webpack
sveltevite, webpack
nextwebpackUses Next.js-specific Webpack presets. See React overview — Next.js.
angularwebpackSupports an options.projectConfig override. See Angular overview.

Community framework definitions (packages named cypress-ct-* or @org/cypress-ct-*) can also be used as the framework value when paired with a supported bundler. See Custom Frameworks.

Automatic bundler configuration detection​

When you use the component.devServer object, Cypress tries to reuse the same bundler configuration you already use to develop your app. You usually do not need to duplicate your entire Vite or Webpack config in your Cypress config file.

Vite​

If you omit viteConfig, Cypress searches upward from your project root for a vite.config.ts|js|mjs|cjs|mts|cts file.

When a config file is found, Cypress loads it and merges in Cypress-specific settings (plugins, public path, spec entries, and file-system allow rules).

If no config file is found, Cypress shows an error asking you to add a vite.config file or pass a viteConfig option explicitly.

Webpack​

If you omit webpackConfig, Cypress searches upward from your project root for a webpack.config.ts|js|mjs|cjs|mts|cts file.

For meta-frameworks like Next.js and Angular, Cypress applies framework-specific presets before merging your config. For React, Vue, and Svelte, Cypress merges your detected Webpack config with a Cypress-specific overlay.

If no Webpack config can be detected and no framework preset applies, Cypress shows an error asking you to add a webpack.config file or pass a webpackConfig option explicitly.

Overriding bundler configuration​

Pass viteConfig or webpackConfig when you need to customize what Cypress uses. For example, to add plugins, tweak aliases, or point to a config file outside the project root.

Both options accept either a config object or an async function that returns a config object.

Vite overrides​

const { defineConfig } = require('cypress')
const customViteConfig = require('./vite.config.custom')

module.exports = defineConfig({
component: {
devServer: {
framework: 'react',
bundler: 'vite',
// Use a specific Vite config object
viteConfig: customViteConfig,
// Or compute one at runtime
viteConfig: async () => {
const base = await import('./vite.config')
return {
...base.default,
// test-only overrides
}
},
},
},
})

Webpack overrides​

const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config')

module.exports = defineConfig({
component: {
devServer: {
framework: 'react',
bundler: 'webpack',
webpackConfig,
webpackConfig: async () => {
const base = await import('./webpack.config')
return {
...base.default,
// test-only overrides
}
},
},
},
})

Function syntax (advanced)​

If you need direct access to the dev server API — for example, to pass options that are not exposed on the object syntax — use the function syntax and import the dev server from the appropriate package:

import { defineConfig } from 'cypress'
import { devServer as viteDevServer } from '@cypress/vite-dev-server'

export default defineConfig({
component: {
devServer(cypressDevServerConfig) {
return viteDevServer({
...cypressDevServerConfig,
framework: 'react',
viteConfig: async () => {
const config = await import('./vite.config')
return config.default
},
})
},
},
})
import { defineConfig } from 'cypress'
import { devServer as webpackDevServer } from '@cypress/webpack-dev-server'

export default defineConfig({
component: {
devServer(cypressDevServerConfig) {
return webpackDevServer({
...cypressDevServerConfig,
framework: 'react',
webpackConfig: require('./webpack.config.js'),
})
},
},
})

The function receives a cypressDevServerConfig object with:

PropertyDescription
specsSpec files Cypress is about to run
cypressConfigThe resolved Cypress configuration
devServerEventsEvent emitter for compile lifecycle events

It must return (or resolve to) an object with:

PropertyDescription
portPort the dev server is listening on
closeOptional callback to shut the server down

You can store additional dev server options on component.devServerConfig when using the function syntax. This field is passed as the second argument to your devServer function.

devServerPublicPathRoute​

The devServerPublicPathRoute option controls the URL path prefix Cypress uses to load compiled specs and assets. It defaults to /__cypress/src.

In most projects the default works well. You may need to change it if component tests reference assets from your app's public directory and those paths must match your Vite base setting. For Vite 5+, set an empty string to align with Vite's default public path:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
devServerPublicPathRoute: '',
devServer: {
framework: 'react',
bundler: 'vite',
},
},
})

Use caution when overriding this value — an incorrect public path can cause specs or assets to fail to load. See the configuration reference for details.

Custom Index File​

By default, Cypress renders your components into an HTML file located at cypress/support/component-index.html.

The index file allows you to add in global assets, such as styles, fonts, and external scripts.

You can provide an alternative path to the file using the indexHtmlFile option in the component config options:

{
component: {
devServer: {
framework: 'react',
bundler: 'vite',
},
indexHtmlFile: '/custom/path/to/component-index.html',
},
}

Fully Custom Dev Server​

If your project uses a bundler other than Vite or Webpack, or you need complete control over compilation, pass a custom function to component.devServer. This is how community integrations and preview-server setups work.

The function receives a single DevServerOptions argument and must return (or resolve to) a ResolvedDevServerConfig describing how Cypress should connect to and stop the server.

interface DevServerOptions {
specs: Cypress.Spec[]
cypressConfig: Cypress.PluginConfigOptions
devServerEvents: NodeJS.EventEmitter
}

interface ResolvedDevServerConfig {
port: number // port the dev server is listening on
close?: (done?: () => void) => void // called by Cypress to shut the server down
}
const { defineConfig } = require('cypress')

module.exports = defineConfig({
component: {
async devServer({ specs, cypressConfig, devServerEvents }) {
const { port, close } = await startDevServer(
specs,
cypressConfig,
devServerEvents
)

return {
port,
close,
}
},
},
})

Any requests triggered during a test using the devServerPublicPathRoute as defined in the cypressConfig will be forwarded to your server. Cypress will trigger a request for [devServerPublicPathRoute]/index.html when a test is started. Your server needs to reply with the html-file referenced in cypressConfig.indexHtmlFile and inject a script to load the support files and the actual test.

function createServer(cypressConfig, bundleDir, port = 1234) {
const app = express()

// read kickstart script - see below for an example
const clientScript = readFileSync(
path.join(__dirname, './client-script.js'),
'utf8'
)

app.get(
cypressConfig.devServerPublicPathRoute + '/index.html',
async (_req, res) => {
// read custom index.html file
const html = await fs.readFile(
path.join(cypressConfig.repoRoot, cypressConfig.indexHtmlFile),
{ encoding: 'utf8' }
)

// inject kickstart-script
const output = html.replace(
'</head>',
`<script type="module">${clientScript}</script></head>`
)
res.send(output)
}
)

// you need to establish some url-to-path-mapping, if your bundler outputs
// the full directory structure you can map this one to one
app.use(cypressConfig.devServerPublicPathRoute, express.static(bundleDir))

app.listen(port)
}

For a real-world example, you can refer to this loader used by the Vite Dev Server.

The client script must retrieve information on the currently active test from the Cypress instance of the parent frame and load the corresponding bundle. If a support file is defined, it should be injected at the top of your test bundle or loaded before the test script.

const CypressInstance = (window.Cypress = parent.Cypress)
const devServerPublicPathRoute = CypressInstance.config(
'devServerPublicPathRoute'
)

let importPromise = Promise.resolve()

// If you do not bundle your support file along with the tests,
// you need to add a separate import statement for the support file.
const supportFilePath = CypressInstance.config('supportFile')
if (supportFilePath) {
const relative = supportFilePath.replace(
CypressInstance.config('projectRoot'),
''
)
importPromise = importPromise.then(
() => import(`${devServerPublicPathRoute}${relative}`)
)
}

// load the spec - you can extend the load function to also load css
const { relative } = CypressInstance.spec
importPromise = importPromise.then(
() => import(`${devServerPublicPathRoute}/${relative}`)
)

// trigger loading the imports
CypressInstance.onSpecWindow(window, importPromise)

// then start the test process
CypressInstance.action('app:window:before:load', window)

For a more complete example you can check out the kickstart script used in the vite-devserver.

The devServerEvents event emitter is used to communicate compile lifecycle events between your server and Cypress:

  • Emit dev-server:compile:success to notify Cypress that a build finished and tests can run.
  • Listen for dev-server:specs:changed to be notified when Cypress updates the set of active spec files (e.g. when a new spec is added), so you can recompile the new entry points.
// signal to Cypress that compilation is done
devServerEvents.emit('dev-server:compile:success')

// recompile when the active spec list changes
devServerEvents.on('dev-server:specs:changed', ({ specs }) => {
recompile(specs)
})

Spec Pattern for Component Tests​

By default, Cypress looks for spec files anywhere in your project with an extension of .cy.js, .cy.jsx, .cy.ts, or .cy.tsx. However, you can change this behavior for component tests with a custom specPattern value. In the following example, we've configured Cypress to look for spec files with those same extensions, but only in the src folder or any of its subdirectories.

{
component: {
devServer: {
framework: 'react',
bundler: 'vite',
},
specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
},
}

Additional Config​

For more information on all the available configuration options, see the configuration reference.