npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jest-styled-components

v7.4.0

Published

Jest utilities for Styled Components

Readme

NPM version CI

jest-styled-components

Testing utilities for styled-components (v5+). Works with Jest, Vitest, and Bun.

styled-components is largely maintained by one person. Please help fund the project for consistent long-term support and updates: Open Collective


The problem: styled-components snapshots contain opaque hashed class names and no CSS rules. When styles change, diffs show meaningless class name changes.

The solution: This library inlines actual CSS rules into snapshots with deterministic class placeholders (c0, c1, k0, k1) and provides a toHaveStyleRule matcher for asserting specific style values.

- Snapshot
+ Received

 .c0 {
-  color: green;
+  color: blue;
 }

 <button
   class="c0"
 />

Table of Contents

Installation

npm install --save-dev jest-styled-components
pnpm add -D jest-styled-components
yarn add -D jest-styled-components
bun add -D jest-styled-components

Setup

Import once in a setup file to register the snapshot serializer and toHaveStyleRule matcher globally.

Jest

// setupTests.js
import 'jest-styled-components'
// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
}

Note: Jest 27+ defaults to the node environment. styled-components requires a DOM, so testEnvironment: 'jsdom' is required. You may also need to install jest-environment-jsdom separately for Jest 28+.

Vitest

// setupTests.ts
import 'jest-styled-components/vitest'
// vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    setupFiles: ['./setupTests.ts'],
  },
})

The Vitest entry point imports expect and beforeEach from Vitest explicitly. TypeScript types are included.

Bun

// setupTests.ts
import 'jest-styled-components'

Bun provides the expect and beforeEach globals that the library hooks into. Configure preload in your bunfig.toml:

[test]
preload = ["./setupTests.ts"]

Quick Example

import React from 'react'
import styled from 'styled-components'
import { render } from '@testing-library/react'
import 'jest-styled-components'

const Button = styled.button`
  color: red;
`

test('it works', () => {
  const { container } = render(<Button />)
  expect(container.firstChild).toMatchSnapshot()
  expect(container.firstChild).toHaveStyleRule('color', 'red')
})

Snapshot Testing

The serializer replaces hashed class names with sequential placeholders (c0, c1 for classes, k0, k1 for keyframes) and prepends the matching CSS rules to the snapshot output. This works with @testing-library/react, react-test-renderer, and Enzyme.

Theming

Wrap with ThemeProvider as you would in your app:

import { ThemeProvider } from 'styled-components'

const theme = { main: 'mediumseagreen' }

test('themed component', () => {
  const { container } = render(
    <ThemeProvider theme={theme}>
      <Button />
    </ThemeProvider>
  )
  expect(container.firstChild).toHaveStyleRule('color', 'mediumseagreen')
})

toHaveStyleRule

expect(element).toHaveStyleRule(property, value?, options?)

Asserts that a CSS property has the expected value on a styled component. The value argument accepts a string, RegExp, asymmetric matcher (e.g. expect.stringContaining()), or undefined to assert the property is not set. When used with .not, value is optional.

const Button = styled.button`
  color: red;
  border: 0.05em solid ${props => props.transparent ? 'transparent' : 'black'};
  cursor: ${props => !props.disabled && 'pointer'};
  opacity: ${props => props.disabled && '.65'};
`

test('default styles', () => {
  const { container } = render(<Button />)
  expect(container.firstChild).toHaveStyleRule('color', 'red')
  expect(container.firstChild).toHaveStyleRule('border', '0.05em solid black')
  expect(container.firstChild).toHaveStyleRule('cursor', 'pointer')
  expect(container.firstChild).not.toHaveStyleRule('opacity')
  expect(container.firstChild).toHaveStyleRule('opacity', undefined)
})

test('prop-dependent styles', () => {
  const { container } = render(<Button disabled transparent />)
  expect(container.firstChild).toHaveStyleRule('border', expect.stringContaining('transparent'))
  expect(container.firstChild).toHaveStyleRule('cursor', undefined)
  expect(container.firstChild).toHaveStyleRule('opacity', '.65')
})

Options

The third argument targets rules within at-rules, with selector modifiers, or by raw CSS selector.

| Option | Type | Description | |---|---|---| | container | string | Match within a @container at-rule, e.g. '(min-width: 400px)' | | layer | string | Match within a @layer at-rule, e.g. 'utilities' | | media | string | Match within a @media at-rule, e.g. '(max-width: 640px)' | | supports | string | Match within a @supports at-rule, e.g. '(display: grid)' | | modifier | string \| css | Refine the selector: pseudo-selectors, combinators, & references, or the css helper for component selectors | | namespace | string | Match rules prefixed by a StyleSheetManager namespace, e.g. '#app' | | selector | string | Match by raw CSS selector instead of component class. Useful for createGlobalStyle |

media and modifier

const Button = styled.button`
  @media (max-width: 640px) {
    &:hover {
      color: red;
    }
  }
`

test('media + modifier', () => {
  const { container } = render(<Button />)
  expect(container.firstChild).toHaveStyleRule('color', 'red', {
    media: '(max-width:640px)',
    modifier: ':hover',
  })
})

supports

const Layout = styled.div`
  @supports (display: grid) {
    display: grid;
  }
`

test('supports query', () => {
  const { container } = render(<Layout />)
  expect(container.firstChild).toHaveStyleRule('display', 'grid', {
    supports: '(display:grid)',
  })
})

container

const Card = styled.div`
  container-type: inline-size;
  @container (min-width: 400px) {
    font-size: 1.5rem;
  }
`

test('container query', () => {
  const { container } = render(<Card />)
  expect(container.firstChild).toHaveStyleRule('font-size', '1.5rem', {
    container: '(min-width: 400px)',
  })
})

layer

const Themed = styled.div`
  @layer utilities {
    color: red;
  }
`

test('layer query', () => {
  const { container } = render(<Themed />)
  expect(container.firstChild).toHaveStyleRule('color', 'red', {
    layer: 'utilities',
  })
})

namespace (StyleSheetManager)

When using StyleSheetManager with a namespace prop, all CSS selectors are prefixed with the namespace. Pass namespace so the matcher knows to expect the prefix:

import { StyleSheetManager } from 'styled-components'

const Box = styled.div`
  color: blue;
`

test('namespaced styles', () => {
  const { container } = render(
    <StyleSheetManager namespace="#app">
      <Box />
    </StyleSheetManager>
  )
  expect(container.firstChild).toHaveStyleRule('color', 'blue', {
    namespace: '#app',
  })
})

To avoid passing namespace on every assertion, set it globally in your setup file:

import { setStyleRuleOptions } from 'jest-styled-components'

setStyleRuleOptions({ namespace: '#app' })

This applies to all subsequent toHaveStyleRule calls. Individual assertions can still override with their own namespace option.

modifier with component selectors

When a rule targets another styled-component, use the css helper:

import { css } from 'styled-components'

const Button = styled.button`
  color: red;
`

const ButtonList = styled.div`
  ${Button} {
    flex: 1 0 auto;
  }
`

test('nested component selector', () => {
  const { container } = render(<ButtonList><Button /></ButtonList>)
  expect(container.firstChild).toHaveStyleRule('flex', '1 0 auto', {
    modifier: css`${Button}`,
  })
})

Class name overrides work similarly:

const Button = styled.button`
  background-color: red;
  &.override {
    background-color: blue;
  }
`

test('class override', () => {
  const { container } = render(<Button className="override" />)
  expect(container.firstChild).toHaveStyleRule('background-color', 'blue', {
    modifier: '&.override',
  })
})

selector (createGlobalStyle)

The selector option matches rules by raw CSS selector, bypassing component class name detection. This is the way to test createGlobalStyle:

import { createGlobalStyle } from 'styled-components'

const GlobalStyle = createGlobalStyle`
  body {
    background: white;
  }
`

test('global styles', () => {
  render(<GlobalStyle />)
  expect(document.documentElement).toHaveStyleRule('background', 'white', {
    selector: 'body',
  })
})

When selector is set, the component argument is ignored---any rendered element will work as the receiver.

Element selection

The matcher checks styles on the element it receives. To assert on nested elements, query for them first:

const { getByTestId } = render(<MyComponent />)
expect(getByTestId('inner-button')).toHaveStyleRule('color', 'blue')

React Native

Import the native entry point instead:

import 'jest-styled-components/native'

This registers only the toHaveStyleRule matcher adapted for React Native's style objects (no snapshot serializer needed). It handles style arrays and converts kebab-case properties to camelCase.

import React from 'react'
import styled from 'styled-components/native'
import renderer from 'react-test-renderer'
import 'jest-styled-components/native'

const Label = styled.Text`
  color: green;
`

test('native styles', () => {
  const tree = renderer.create(<Label />).toJSON()
  expect(tree).toHaveStyleRule('color', 'green')
})

Advanced Usage

Standalone Serializer

The serializer can be imported separately for use with libraries like jest-specific-snapshot:

import { styleSheetSerializer } from 'jest-styled-components/serializer'
import { addSerializer } from 'jest-specific-snapshot'

addSerializer(styleSheetSerializer)

Serializer Options

import { setStyleSheetSerializerOptions } from 'jest-styled-components/serializer'

setStyleSheetSerializerOptions({
  addStyles: false,                          // omit CSS from snapshots
  classNameFormatter: (index) => `styled${index}`,  // custom class placeholders
})

CSS Parse Caching

By default, toHaveStyleRule re-parses the stylesheet on every assertion. For test suites with many assertions, import the cached entry point to parse once and reuse the result when the stylesheet hasn't changed:

import 'jest-styled-components/cache'

The cache automatically invalidates when the stylesheet changes and when resetStyleSheet runs between tests. No manual cleanup needed.

resetStyleSheet

The main entry point calls resetStyleSheet() in a beforeEach hook automatically. If you use the standalone serializer or a custom test setup where beforeEach is not globally available, call it manually:

import { resetStyleSheet } from 'jest-styled-components'

resetStyleSheet()

Version Compatibility

| jest-styled-components | styled-components | Test Runner | |---|---|---| | 7.2+ | 5.x, 6.x | Jest 27+, Vitest 1+, Bun | | 7.0--7.1 | 5.x | Jest 27+ | | 6.x | 4.x--5.x | Jest 24--26 |

Troubleshooting

"No style rules found on passed Component"

The most common issue. Check these causes in order:

  1. Wrong element. The matcher needs the actual DOM element or react-test-renderer JSON node, not a wrapper. With @testing-library/react, use container.firstChild. With Enzyme, use mount() (not shallow() for most cases).
  2. Multiple styled-components instances. Common in monorepos. Run npm ls styled-components (or pnpm why styled-components) to check. See the styled-components FAQ for resolution.
  3. Version mismatch. Ensure your jest-styled-components version supports your styled-components version (see Version Compatibility).

TypeScript: toHaveStyleRule not recognized

The package ships type declarations that augment Jest's Matchers interface. If TypeScript doesn't pick them up:

Jest: Ensure your tsconfig.json includes the package in types, or that your setup file import (import 'jest-styled-components') is within the TypeScript project's include paths.

Vitest: Import from jest-styled-components/vitest (not the base entry point). This augments Vitest's Assertion interface.

Styles missing from snapshots

If snapshots show hashed class names instead of CSS rules, the serializer isn't registered. Verify:

  1. You're importing jest-styled-components (or jest-styled-components/vitest for Vitest) in your setup file.
  2. The setup file is referenced in your test runner config (setupFilesAfterEnv for Jest, setupFiles for Vitest).
  3. You don't have multiple instances of styled-components loaded (see above).

Testing createGlobalStyle

Global styles don't attach to a specific component. Use the selector option to match by CSS selector:

render(<GlobalStyle />)
expect(document.documentElement).toHaveStyleRule('background', 'white', {
  selector: 'body',
})

Jest environment errors (document is not defined)

Jest 27+ defaults to the node test environment. Add testEnvironment: 'jsdom' to your Jest config. For Jest 28+, install jest-environment-jsdom as a separate dependency.

Legacy: Enzyme

Enzyme is no longer actively maintained. If you still use it, snapshot testing requires enzyme-to-json and toHaveStyleRule works with both shallow and mounted wrappers. Consider migrating to @testing-library/react.

Contributing

Open an issue to discuss before submitting a PR.

License

Licensed under the MIT License.