styled-atom
v3.0.3
Published
Runtime for loading and mounting CSS atoms in React
Maintainers
Readme

Table of contents
About
styled-atom is a tiny TypeScript runtime for mounting CSS atoms only when a React preview, component or tool surface needs them.
It is designed for React demo shells, component workbenches, visual editors and render-heavy UI sandboxes: places where many small previews mount, unmount and re-render while sharing CSS files or small inline style atoms.
It is not a CSS framework. It does not create cascade layers or replace your bundler. It owns the runtime part: loading CSS by name, compiling small React-like style objects, injecting stable style tags, tracking loading state and releasing styles when nobody uses them anymore.
The idea is simple - imported CSS goes through a React-bound runtime store, while inline CSS can use StyledAtom directly.
Installation
npm install styled-atomimport StyledAtom, { createStyledAtomStore } from "styled-atom";✦ Note:
- Supports both ESM (
import) and CommonJS (require) builds.- Written in TypeScript and ships declaration files.
- React is a peer dependency.
- The package does not ship runtime CSS. Your project keeps ownership of CSS files and the loader function.
API
There are two StyledAtom entry points:
import StyledAtom from "styled-atom"- a standalone inline style atom. It does not need a store or loader and acceptsname+styles.styleAtomsStore.StyledAtom- a store-bound style atom returned bycreateStyledAtomStore. It acceptsfilesand loads CSS through the store loader.
Description: Mounts one inline CSS atom without creating a store. The component compiles a React-like style object into an owned <style> tag and releases it on unmount. It does not render a wrapper by default. Add encap when the atom should wrap children and compile styles through that wrapper selector.
Props:
name: string- inline atom name used for the style tag, default root selector and devsourceURL.styles: StyledAtomStyles- React-like CSS object.encap?: boolean | string | StyleEncap- optional wrapper scope. Omitted means no wrapper.encap={true}uses thenameclass. String,className,idandattributeuse the selector you provide and the inline CSS is compiled under that selector.fallback?: React.ReactNode- content rendered before the style atom is mounted.onLoad?: () => void- called once when this atom changes from loading to loaded.children?: React.ReactNode- content shown after the atom is mounted.
Example:
import StyledAtom from "styled-atom";
export function LoadingScreen() {
return (
<StyledAtom
name="loading-screen"
encap
styles={{
backgroundColor: "#fff",
minHeight: "100vh",
".title": {
color: "#111",
fontWeight: 600,
},
"@media (max-width: 640px)": {
padding: 16,
},
}}
>
<section>
<h1 className="title">Loading</h1>
</section>
</StyledAtom>
);
}Style object:
In the styles prop nested selectors are resolved from the root selector. Without encap the root selector is .${name}, so add that class yourself when root declarations should apply to your markup. With encap, the root selector is the generated wrapper selector. Selector lists are scoped per item, so a nested selector list such as .card, .panel inside .dark compiles both selectors under .dark. At-rules such as @media and @keyframes are supported. Numeric values receive px unless the CSS property is unitless. For pseudo-element content, pass the literal text directly (content: "", content: "×"); existing CSS values such as content: '""', attr(...), counter(...) and none are preserved.
Description:
The store-bound component registers requested files in the shared runtime, renders fallback while the loader resolves them and reuses already mounted style tags with other atoms from the same store.
Example:
import StyledAtomImport from "your-styled-atom-store";
<StyledAtomImport
files={["reset", "preview-card"]}
fallback={<span>Loading...</span>}
>
<PreviewCard />
</StyledAtomImport>;Props:
files?: string | readonly string[]- CSS atom names passed to the configured loader.encap?: boolean | string | StyleEncap- optional wrapper props only. Omitted means no wrapper.encap={true}adds classes fromfiles; string,className,idandattributeuse the selector you provide. Imported CSS is not rewritten, so files should target that wrapper themselves.fallback?: React.ReactNode- content rendered while requested files are loading.onLoad?: () => void- called once when this atom changes from loading to loaded.children?: React.ReactNode- content shown after the requested files are loaded.
Description: Creates one React-bound style runtime and a StyledAtom component bound to that runtime. Use one runtime for a shell, workspace or isolated UI surface. Every mounted atom shares the same CSS cache and loader.
Return: Returns the bound component and a small runtime control surface:
const { StyledAtom, configure, reload, replace, dispose } =
createStyledAtomStore();StyledAtom- React component that loads requested CSS files before rendering children.configure(path)- set or replace the CSS loader.reload(files?)- reload all or selected mounted CSS atoms through the configured loader.replace(styles)- replace mounted CSS text directly without calling the loader.dispose()- remove all style tags owned by this runtime.
Example:
import { createStyledAtomStore } from "styled-atom";
export const styleAtomsStore = createStyledAtomStore(
(name) => import(`./styles/${name}.css`),
);
export const StyledAtomImport = styleAtomsStore.StyledAtom;Patterns
import StyledAtom, { type StyledAtomStyles } from "styled-atom";
const splashStyles: StyledAtomStyles = {
display: "grid",
placeItems: "center",
minHeight: "100vh",
backgroundColor: "#fff",
".logo": {
width: 96,
},
};
export function SplashScreen() {
return (
<StyledAtom name="splash-screen" encap styles={splashStyles}>
<img className="logo" src="/logo.svg" alt="" />
</StyledAtom>
);
}export function HostStyledShell({ children }) {
return (
<>
<StyledAtomImport files={["reset", "theme"]} />
{children}
</>
);
}The style tags are released automatically when the StyledAtom unmounts and no other mounted atom uses the same files.
// Any dev server, watcher or bundler integration
styleAtomsStore.replace([
{
file: changedFileName,
css: nextCssText,
},
]);changedFileName and nextCssText usually come from a dev server, bundler plugin, file watcher or custom preview infrastructure.
Only the provided CSS atoms are replaced. Mounted React previews stay in place, and unrelated style entries are left untouched.
If CSS text is not available, ask the configured loader to fetch fresh CSS instead:
// Reload selected mounted atoms through the configured loader.
styleAtomsStore.reload(["main", "card"]);
// Reload every currently mounted atom.
styleAtomsStore.reload();