React Cosmos ✭ Dev tool for creating reusable React components
Cosmos scans your project for components and loads them inside Component Playground, enabling you to:
- Render components under any combination of props, context and state
- See state evolve in real-time while interacting with running instances
Working with Cosmos improves component design because it surfaces implicit dependencies. It also forces you to define sane inputs for your components, making them predictable and easier to debug down the road.
Read the story of React Cosmos: Fighting for Component Independence
- React >=0.14.9
- webpack or Browserify (or go rogue and roll your own integration)
- Fixtures to define states for your components (you'll do this after you get started)
React Cosmos works best with webpack. Making it work with other bundlers takes extra work, but a complete Browserify example is available.
Jump to:
- Getting started
- Fixtures
- Proxies
- Integration with popular tools
- Configuration
- Exporting
- Experimental: Test helpers
- Why Cosmos and not X?
Have a question or idea to share? See you on Slack.
npm install --dev react-cosmos-webpack
# or
yarn add --dev react-cosmos-webpackAdd cosmos.config.js to your project root
module.exports = {
componentPaths: ['src/components'],
// Optionally, reuse loaders and plugins from your existing webpack config
webpackConfigPath: './config/webpack.config.dev'
};Add script to package.json
"scripts": {
"cosmos": "NODE_ENV=development cosmos"
}Run npm run cosmos or yarn cosmos and go to localhost:8989 🎉
- See popular integrations (e.g. CRA or Next.js)
- Extend your config
- Create fixtures
- Configure or create proxies (e.g. Redux)
- Be kind and report what went wrong
A fixture is a component mock. An example of component input. The input can be props, state, or–with the help of proxies–anything a component depends upon (e.g. API responses).
export default {
props: {
value: 'Lorem ipsum',
disabled: true,
onChange: value => console.log(`Select: ${value}`)
}
}Follow this guide to get started with fixtures.
Cosmos looks for __fixtures__ dirs next to your components. Here are a few of the supported patterns.
components/Button.jsx
components/nested/Dropdown.jsx
components/__fixtures__/Button/default.js
components/__fixtures__/Button/disabled.js
components/__fixtures__/nested/Dropdown/default.js
components/__fixtures__/nested/Dropdown/open.jscomponents/Button/index.jsx
components/Button/__fixtures__/default.js
components/Button/__fixtures__/disabled.js
components/nested/Dropdown/index.jsx
components/nested/Dropdown/__fixtures__/default.js
components/nested/Dropdown/__fixtures__/open.jsNamed component files also work in the nested hierarchy (i.e. Button/Button.jsx and nested/Dropdown/Dropdown.jsx).
Proxies are Cosmos plugins, allowing fixtures to go beyond mocking props and state. As regular React components, proxies compose in the order they are listed in your config and decorate the loaded component, respecting the contract to render the next proxy in the chain.
The added functionality can range from mocking Redux state to creating a resizable viewport for seeing how components behave at different scales.
Most components in a Redux app depend on Redux state–either they're a container or one of their descendants is. This proxy creates the store context required for any component you load, just like Provider does for your root component. Writing Redux fixtures almost feels too easy. Because Redux state is global, once you have one state mock you can render any component you want!
// redux-proxy.js
import createReduxProxy from 'react-cosmos-redux-proxy';
export default () => {
return createReduxProxy({
// Called when fixture loads with fixture.reduxState as initial state.
// See https://github.com/skidding/flatris/blob/master/cosmos/redux-proxy.js
createStore: (initialState) => {
return Redux.createStore(yourReducer, initialState, yourMiddleware);
},
})
};Very convenient if your app uses React context. You can provide generic context using a base fixture that all other fixtures extend.
// context-proxy.js
import createContextProxy from 'react-cosmos-context-proxy';
export default () => {
return createContextProxy({
// Expects fixture.context to contain `theme` object
// See examples/context
childContextTypes: {
theme: PropTypes.object.isRequired,
},
});
};Besides client-side state, components also depend on external data. Mocking server responses allows us to completely isolate our components. By adding the react-cosmos-xhr-proxy to our config, we can put XMLHttpRequest mocks in fixtures.
// __fixtures__/example.js
export default {
xhr: [
{
url: '/users',
response: (req, res) =>
res.status(200).body([
{
id: 1,
name: 'Blossom',
},
{
id: 2,
name: 'Bubbles',
},
{
id: 3,
name: 'Buttercup'
}
]),
},
],
};The proxy is a thin layer on top of the xhr-proxy utility. Check out the Axios example to see it in action.
Like the XHR proxy, but for the Fetch API.
// __fixtures__/example.js
export default {
fetch: [
{
matcher: '/users',
response: [
{
id: 1,
name: 'Prabu',
},
{
id: 2,
name: 'Karla',
},
{
id: 3,
name: 'Linbaba'
}
],
},
]
};Built on top of fetch-mock. Check out the Fetch example to see react-cosmos-fetch-proxy in action.
react-cosmos-router-proxy is designed for React Router v4 and above
React Router is used in most React projects. Wrapping components with withRouter makes the Router context an implicit dependency–one we need to mock. But mocking RR internals and putting them into the React context is nasty business. No worries, react-cosmos-redux-proxy does it for you.
Simply adding a url to your fixture will wrap the loaded component inside a Router.
// __fixtures__/example.js
export default {
url: '/about'
}Optionally, route can be added to also wrap the loaded component inside a Route.
// __fixtures__/example.js
export default {
url: '/users/5',
route: '/users/:userId'
}Check out the React Router example to see react-cosmos-redux-proxy in action.
What proxy would you create to improve DX?
It's preferred to use CRA's own webpack config (instead of duplicating it).
// cosmos.config.js
module.exports = {
componentPaths: ['src/components'],
containerQuerySelector: '#root',
webpackConfigPath: 'react-scripts/config/webpack.config.dev',
publicPath: 'public'
};Also make sure to:
- Run
cosmoswithNODE_ENV=development - Put proxies in the
srcdir–the only place included by the CRA Babel loader
CRA + Cosmos example: Flatris
Next.js apps run on both client & server, so compilation is done via Babel plugins instead of webpack loaders. This means we can rely on Cosmos' default webpack config.
// cosmos.config.js
module.exports = {
componentPaths: ['components'],
publicPath: 'static',
publicUrl: '/static/',
};Define .babelrc for the Cosmos webpack config to rely on the Next.js preset.
{
"presets": ["next/babel"],
}Next.js + Cosmos example: Illustrated Algorithms
It's preferred to use the starter kit's own webpack config (instead of duplicating it).
// cosmos.config.js
module.exports = {
componentPaths: [
'src/routes/Counter/components',
'src/routes/Home/components'
],
webpackConfigPath: 'build/webpack.config.js'
}Also make sure to:
- Run
cosmoswithNODE_ENV=development - Set up the Redux proxy :)
GET http://localhost:8989/loader/index.html 404 (Not Found)
The browser console might greet you with this error when using a custom webpack config. There are two methods for configuring the Loader index.html page:
- Use html-webpack-plugin
- Put a static index.html file in your public path (see
publicPathoption below)
Using html-webpack-plugin is recommended because it automatically injects the <script> tag in index.html. If you create your own index.html then make sure the script tag points to "main.js".
<script src="./main.js"></script>Use the --config CLI arg if you prefer not placing the config in the project root.
// package.json
"scripts": {
"cosmos": "NODE_ENV=development cosmos --config path/to/cosmos.config.js"
}The componentPaths option supports both dir and file paths. Most of the times using a dir path is enough (e.g. "src/components"), but we might need to target the exact file paths when component files sit next to non-component files. Here's an example for this type of setup:
// cosmos.config.js
componentPaths: [
'src/components/A.jsx',
'src/components/B.jsx'
],
getComponentName: componentPath =>
componentPath.match(/src\/components\/(.+)\.jsx$/)[1]The
getComponentNameoption needs to be added when using file paths.
We can also use glob instead of adding each component by hand:
// cosmos.config.js
componentPaths: glob.sync('src/components/*.jsx')Options supported by cosmos.config.js.
// cosmos.config.js
module.exports = {
// Read components from multiple locations. Useful for including Redux
// containers or if you split your UI per sections.
componentPaths: [
'src/components',
'src/containers'
],
// Additional paths to search for fixtures, besides the __fixtures__ folder
// nested inside component paths. Useful if you keep fixture files separated
// from components files.
fixturePaths: [
'test/fixtures'
],
// Additional entry points that should be present along with any component.
// Sad, but inevitable.
globalImports: [
'./reset.css',
'./global.css',
],
// Components will not be loaded in the playground if their names match these.
// There's no excuse for components that can't be loaded independently, but
// if you store HoCs (which export functions) next to regular components, well,
// what are you gonna do, not use this wonderful tool?
ignore: [
/notATrueComponent/,
/itsComplicated/,
/itsNotMeItsYou/,
],
// Where to serve static files from. Like --content-base in webpack-dev-server.
publicPath: 'src/public',
// Set base URL for static assets from public folder
publicUrl: '/static/',
// Read more about proxies below
proxies: [
'./redux-proxy.js',
'./context-proxy.js',
],
// Render inside custom root element. Useful if that root element already
// has styles attached, but bad for encapsulation.
containerQuerySelector: '#app',
// Disable hot module replacement
hot: false,
// These ones are self explanatory
hostname: 'localhost',
port: 8989,
webpackConfigPath: './config/webpack.config.dev',
};Static Component Playground? Piece of 🍰! Add this script and run npm run cosmos-export or yarn cosmos-export.
// package.json
"scripts": {
"cosmos-export": "NODE_ENV=production cosmos-export"
}Fixtures can be reused inside automated tests. Along with proxies, they replace elaborate test preparation and render a component with a single JSX tag.
import { mount } from 'enzyme';
import { Loader } from 'react-cosmos-loader';
import DisplayScreen from '../';
import fixture from '../__fixtures__/hi-there';
it('should render hello message', () => {
const wrapper = mount(
<Loader component={DisplayScreen} fixture={fixture} />
);
expect(wrapper.text()).toMatch(/Hi there/);
});You can create a snapshot of all your components with react-cosmos-telescope. A single snapshot file for all components isn't ideal, but it makes a difference until you have time to create granular tests.
import runTests from 'react-cosmos-telescope';
runTests({
cosmosConfigPath: require.resolve('./cosmos.config.js'),
});Many other component explorers emerged in the last few years. React Storybook and React Styleguidist are good examples, but you can find a more extensive list of options here. To decide which tool is best for you, check for each project's goals, how much they match your needs and how well the project is maintained.
Cosmos is a dev tool first, made to improve the design of all components, big and small, not just the stateless UI bits. The fixture and proxy architecture doubles as an automated testing utility and further aids the process of developing robust and reusable components. Cosmos also makes it easy to create a living style guide, but it's a secondary goal and you might get more value from alternatives if this is your chief concern.
This project welcomes all. Check out the Contributing Guide to read about the project's mission and how to get involved. Ask anything on Slack. Let's make UI development fun!
Thanks to Kreativa Studio for the Cosmos logo.

