skip to content

What is Cypress's component-index.html, and what must it contain for cy.mount to work?

level: middleimportance: should knowfreq 52%

answer

  1. A component still needs a document
  2. One attribute the adapter looks for
  3. Default sits beside the support file
  4. A config key relocates the whole page
  5. The error names the missing selector

basics

~20 s

It is the HTML page Cypress renders components into, at cypress/support/component-index.html by default. It must contain an element carrying the data-cy-root attribute; the mount adapters query for it and throw if it is missing. The indexHtmlFile option moves the page.

solid answer

~40 s

A component test still needs a document, and `component-index.html` is it - a near-empty page Cypress serves in the frame where your test runs, with the compiled support file and spec injected into it. The scaffolded version is a bare HTML skeleton whose body holds one element: `<div data-cy-root></div>`. Every mount adapter resolves its container with the attribute selector `[data-cy-root]`, so if that element is absent the mount throws *No element found that matches selector [data-cy-root]* rather than rendering. The tag and any classes are irrelevant; only the attribute is looked up. The default path is `cypress/support/component-index.html`, and the `indexHtmlFile` option under `component` in the Cypress config points at a different file - useful when a design system wants its own font links or reset around every mounted component.

code

javascript · 9 lines
javascript
// cypress.config.js
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  component: {
    devServer: { framework: 'react', bundler: 'vite' },
    indexHtmlFile: 'cypress/support/design-system-index.html',
  },
})

go deeper

for a junior

Know that Cypress renders components into cypress/support/component-index.html and that the page must contain an element carrying the data-cy-root attribute.

for a middle

Explain the lookup itself: the adapter queries for [data-cy-root] and throws a named error when nothing matches, and indexHtmlFile moves the page without changing anything else.

for a senior

Expect questions about diagnosing the missing-root failure in CI, and about what genuinely belongs on a page shared by every component test versus what belongs in one spec.

for a principal

Decide whether a component library needs one harness page or several, and be able to argue what a per-package page costs in drift against what it buys in fidelity.

## What the page is for Cypress component testing is not a simulated DOM. The component is rendered by a real browser, which means there has to be a real page for it to be rendered into. `component-index.html` is that page. Cypress serves it in the frame where your test runs, and the dev server injects the compiled support file and the compiled spec into it. Your application's own `index.html` is never involved: no app bootstrap script, no router, no store - just an empty document waiting for `cy.mount` to put something in it. The scaffolded file is deliberately minimal: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>Components App</title> </head> <body> <div data-cy-root></div> </body> </html> ``` ## The one required element Everything hangs on `data-cy-root`. Each adapter resolves the container it renders into by querying the document for the attribute selector `[data-cy-root]`, and if nothing matches it throws before rendering anything: > No element found that matches selector [data-cy-root]. Please add a root element with data-cy-root attribute to your "component-index.html" file so that Cypress can attach your component to the DOM. Practical consequences of that lookup: - It is an **attribute**, not an id or a class. `<div id="cy-root">` and `<div class="data-cy-root">` both fail. - The **tag does not matter**. Any element carrying the attribute is a valid host; the scaffold uses a bare `div` only because it adds nothing of its own. - **The first match wins**, so a second `data-cy-root` element further down the page is simply never used. - The adapter **empties and reuses** that element. It is a socket, not a container you populate yourself. ## Relocating it with indexHtmlFile Two `component` options describe the harness, and it is worth keeping them straight: | Option | Default | What it points at | |---|---|---| | `indexHtmlFile` | `cypress/support/component-index.html` | the page components are rendered into | | `supportFile` | `cypress/support/component.js` | the file that registers `cy.mount` | Setting `indexHtmlFile` is a path swap and nothing more: the new file still has to carry a `data-cy-root` element, and the support file still has to register `cy.mount`. The path is resolved from the Cypress project root, which is the detail that bites in a monorepo. ## Failure modes you will actually hit 1. **Every spec fails identically with the root-element error.** Somebody edited the index page - usually to add a wrapper around the root - and dropped the attribute. The error names the selector; grep the file for it. 2. **One package in a monorepo fails and the rest pass.** Because the path is resolved from the Cypress project root, a project rooted somewhere other than where you assumed points at a page that does not exist, or at the wrong one. 3. **A component looks unstyled in the runner but fine in the app.** The harness page is not your application shell, so anything the app's real `index.html` supplied - a font link, a theme class on `<body>` - is simply absent here unless you put it on this page. 4. **Layout differs from production because the root is not where the component expects.** A `Toast` positioned with `position: fixed` behaves differently when the root element sits inside a transformed ancestor you added to the page. Keep the wrapper markup near-zero unless you have a reason. ## What it is not - It is **not** a place to bootstrap the application. Nothing on the page should mount a router, a store or an app shell; the component under test should be the only thing rendered. - It is **not** where you configure the run. Timeouts, the spec pattern and the dev server all live in the Cypress config, not in this page. - It is **not** per-spec. One page serves every component spec in the project, which is exactly why anything you add to it is a shared cost paid by every test in the suite. The page is best understood as a stage with one marked spot on it. Cypress hands the framework that spot; everything the test then sees is put there by `cy.mount`, and everything else on the page is scenery you are responsible for justifying.

  • What does Cypress do with component-index.html at run time?
    It serves that page in the frame where the test runs, with the compiled support file and spec injected into it, and the mount adapter then renders your component into the element carrying `data-cy-root`. Your application's own index page plays no part - there is no app bootstrap, no router and no store unless a test puts one there.
  • Does the root element have to be a div with data-cy-root?
    Any element works. The adapters resolve the container with the attribute selector `[data-cy-root]`, so the tag is irrelevant and extra classes or ids change nothing. The scaffolded page uses a bare `div` because the component's own markup should decide everything else about the layout.

The index page is an empty stage with one marked spot on it. Cypress hands the framework that spot - the element carrying data-cy-root - and everything the audience sees is put there by cy.mount.

saying these in an interview costs you the question

  • Thinks Cypress renders components into the application's real index.html
  • Adds an id or class named cy-root instead of the attribute
  • Believes indexHtmlFile changes which spec files run
  • Blames the dev server for a missing root element error