React Cosmos: Sandbox for Developing and Testing React Components in Isolation

React Cosmos: Sandbox for Developing and Testing React Components in Isolation

React Cosmos is a sandbox for developing React components in isolation. Unlike Storybook and Ladle, Cosmos focuses on interactive development: you can manipulate component state in real time, inspect props changes, and mock context providers. It also integrates with testing tools so fixtures (Cosmos's name for stories) can drive unit tests.

Installation

npm install --save-dev react-cosmos

Add a script:

{
  "scripts": {
    "cosmos": "cosmos",
    "cosmos:export": "cosmos-export"
  }
}

Cosmos reads your project's Vite or webpack config automatically. No separate bundler setup needed.

Fixtures

Fixtures are the core unit in Cosmos. A fixture is either a React element or a function component with no props:

// Button.fixture.tsx
import { Button } from './Button';

// Simple element fixture
export default <Button variant="primary">Click me</Button>;

Multiple named fixtures in one file:

// Button.fixture.tsx
import { Button } from './Button';

export const Primary = () => <Button variant="primary">Primary</Button>;
export const Secondary = () => <Button variant="secondary">Secondary</Button>;
export const Danger = () => <Button variant="danger">Delete</Button>;
export const Loading = () => <Button variant="primary" loading>Loading...</Button>;
export const Disabled = () => <Button variant="primary" disabled>Disabled</Button>;

Each export appears as a separate fixture in the Cosmos UI.

useValue — Real-Time State Editing

useValue is Cosmos's killer feature: it renders an editable control in the Cosmos UI that updates the component in real time.

// SearchBar.fixture.tsx
import { useValue } from 'react-cosmos/client';
import { SearchBar } from './SearchBar';

export default function SearchBarFixture() {
  const [query, setQuery] = useValue('query', { defaultValue: '' });
  const [placeholder, setPlaceholder] = useValue('placeholder', {
    defaultValue: 'Search...',
  });
  const [isLoading, setIsLoading] = useValue('isLoading', { defaultValue: false });

  return (
    <SearchBar
      value={query}
      placeholder={placeholder}
      isLoading={isLoading}
      onChange={setQuery}
    />
  );
}

When this fixture runs in the Cosmos UI:

  • A text input appears for query
  • A text input for placeholder
  • A checkbox for isLoading

Changing any control updates the component immediately. The current state is also visible in the props panel.

useSelect — Dropdown Controls

For controlled state with a fixed set of options:

import { useSelect, useValue } from 'react-cosmos/client';
import { Alert } from './Alert';

export default function AlertFixture() {
  const [type] = useSelect('type', {
    options: ['info', 'warning', 'error', 'success'],
    defaultValue: 'info',
  });
  const [message] = useValue('message', { defaultValue: 'Something happened' });
  const [dismissible] = useValue('dismissible', { defaultValue: true });

  return <Alert type={type} dismissible={dismissible}>{message}</Alert>;
}

Decorators

Decorators wrap fixtures in providers or layouts. Create a cosmos.decorator.tsx file in a directory to apply it to all fixtures in that directory and below:

// src/cosmos.decorator.tsx
import { Decorator } from 'react-cosmos/client';
import { ThemeProvider } from './theme';
import { AuthProvider } from './auth';
import './global.css';

const GlobalDecorator: Decorator = ({ children }) => (
  <ThemeProvider>
    <AuthProvider>
      {children}
    </AuthProvider>
  </ThemeProvider>
);

export default GlobalDecorator;

Cosmos looks for cosmos.decorator.tsx files up the directory tree and applies all of them, outermost first. A decorator in src/ wraps every fixture; a decorator in src/admin/ only wraps fixtures in that directory.

Mocking Context with useFixtureSelect

For fixtures that need to test different authentication states:

// Dashboard.fixture.tsx
import { useFixtureSelect } from 'react-cosmos/client';
import { AuthContext } from '../auth/AuthContext';
import { Dashboard } from './Dashboard';

export default function DashboardFixture() {
  const [role] = useFixtureSelect('user role', {
    options: ['admin', 'viewer', 'none'],
  });

  const user = role === 'none' ? null : { name: 'Alice', role };

  return (
    <AuthContext.Provider value={{ user, isLoading: false }}>
      <Dashboard />
    </AuthContext.Provider>
  );
}

Configuration

cosmos.config.json in the project root:

{
  "rootDir": "src",
  "fixturesDir": "__fixtures__",
  "fixtureFileSuffix": "fixture",
  "staticPath": "public",
  "port": 5000,
  "plugins": []
}

Or use cosmos.config.ts for TypeScript:

import { CosmosConfig } from 'react-cosmos';

const config: CosmosConfig = {
  rootDir: 'src',
  port: 5000,
};

export default config;

Using Fixtures in Tests

Import fixtures into React Testing Library tests:

// Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Primary, Disabled } from './Button.fixture';

test('primary button is clickable', () => {
  const onClick = vi.fn();
  render(<Primary onClick={onClick} />);
  fireEvent.click(screen.getByRole('button'));
  expect(onClick).toHaveBeenCalled();
});

test('disabled button does not fire click', () => {
  const onClick = vi.fn();
  render(<Disabled onClick={onClick} />);
  fireEvent.click(screen.getByRole('button'));
  expect(onClick).not.toHaveBeenCalled();
});

Simple element fixtures (not using useValue) are plain React elements and import cleanly into any test.

Remote Renderer

The Cosmos remote renderer embeds a Cosmos UI inside your running application. This is useful for testing components in a real app environment (real router, real API calls) rather than an isolated sandbox:

// In your app's entry point (dev only):
if (process.env.NODE_ENV === 'development') {
  const { RemoteRendererServer } = await import('react-cosmos/remote');
  // Cosmos UI connects to this renderer
}

The remote renderer is useful when components have deep dependencies on app-level context that's hard to mock.

Exporting Static Fixtures

npm run cosmos:export

Outputs a static site with all fixtures. Use it as a component catalog that non-developers can browse.

Key Points

  • Fixtures are React elements or function components — simpler than Storybook story objects
  • useValue creates editable controls in the Cosmos UI that update components in real time
  • useSelect adds dropdown controls for multi-option props
  • cosmos.decorator.tsx files apply providers and layouts to fixtures in their directory scope
  • Import simple fixtures directly into RTL tests — no special adapter needed
  • The remote renderer lets you develop components inside a running app with real context
  • cosmos-export produces a static site for sharing component catalogs

Read more