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-cosmosAdd 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:exportOutputs 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
useValuecreates editable controls in the Cosmos UI that update components in real timeuseSelectadds dropdown controls for multi-option propscosmos.decorator.tsxfiles 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-exportproduces a static site for sharing component catalogs