React Cosmos with Remix
Rasmus Piho
Posted on December 9, 2021
I recently discovered https://remix.run. Whoa, haven't been this excited over a framework for a long time. Naturally I already switched some pet projects over and the development flow has been very straightforward.
One topic of interest of mine is how to speed up and isolate developing of components that I use in my apps.
Enter https://reactcosmos.org. It's an alternative to Storybook and to me looks a bit cleaner with smaller amount of boilerplate needed to get running out of the box. It runs a separate dev server with a clean UI displaying all of your component fixtures.
So I tried to pair my Remix app with React Cosmos so I would not have to run a separate Webpack bundler in order to get the fixtures update as I work on the components. I got it working by following the Snowpack example from React-Cosmos Github space.
The first draft of this tutorial I posted also under an open issue about supporting StoryBook in Remix Github issues page: https://github.com/remix-run/remix/issues/214
Create cosmos.tsx
under app/routes
:
export default function Cosmos() {
return null;
}
Add cosmos.config.json
in your project root directory:
{
"staticPath": "public",
"watchDirs": ["app"],
"userDepsFilePath": "app/cosmos.userdeps.js",
"experimentalRendererUrl": "http://localhost:3000/cosmos"
}
Change your entry.client.tsx
accordingly:
import { mountDomRenderer } from "react-cosmos/dom";
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
import { decorators, fixtures, rendererConfig } from "./cosmos.userdeps.js";
if (
process.env.NODE_ENV === "development" &&
window.location.pathname.includes("cosmos")
) {
mountDomRenderer({ rendererConfig, decorators, fixtures });
} else {
hydrate(<RemixBrowser />, document);
}
You might need to add // @ts-nocheck
in the beginning of this file if using Typescript (you should), because TS will likely complain about not finding ./cosmos.userdeps.js
which will be generated automatically by React Cosmos on each run. Oh and you should add that file to your .gitignore
file as well!
Of course, add react-cosmos as a dev dependency:
$ npm i -D react-cosmos
Add the following in your package.json
scripts section:
"cosmos": "cosmos",
"cosmos:export": "cosmos-export"
Start the remix dev server:
$ npm run dev
Start cosmos server in another terminal window:
$ npm run cosmos
Now, although this works, I noticed in the developer console, that my remix app started polling itself and getting 404 periodically because of a socket.io route was not configured.
This started to bother me so I investigated further and found a cleaner solution:
In app/routes/cosmos.tsx
make the following changes:
import { useCallback, useState } from "react";
import { useEffect } from "react";
import { HeadersFunction } from "remix";
import { decorators, fixtures, rendererConfig } from "~/cosmos.userdeps.js";
const shouldLoadCosmos =
typeof window !== "undefined" && process.env.NODE_ENV === "development";
export const headers: HeadersFunction = () => {
return { "Access-Control-Allow-Origin": "*" };
};
export default function Cosmos() {
const [cosmosLoaded, setCosmosLoaded] = useState(false);
const loadRenderer = useCallback(async () => {
const { mountDomRenderer } = (await import("react-cosmos/dom")).default;
mountDomRenderer({ decorators, fixtures, rendererConfig });
}, []);
useEffect(() => {
if (shouldLoadCosmos && !cosmosLoaded) {
loadRenderer();
setCosmosLoaded(true);
}
}, []);
return null;
}
And restore your entry.client.ts
file to it's original state:
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
hydrate(<RemixBrowser />, document);
So there you have it - Remix dev server running on localhost:3000 and React Cosmos server running on localhost:5000.
Notice the headers function export in app/routes/cosmos.tsx
- I added that so there would be no annoying cors errors in your dev console, although it seemed to work perfectly fine without it as well.
Posted on December 9, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.