macaron pulls CSS-in-JS out of the bundle with a compile step, not a runtime
Compiler-augmented typesafe CSS-in-JS with zero runtime, colocation, maximum safety and productivity
At a glance
- What is it?
- Macaron is a TypeScript-first CSS-in-JS library whose styles are extracted at build time, so the styled-components and vanilla-extract style APIs cost nothing at runtime. It supports React, Solid and Qwik, and its last tagged release was v1.6.1 in October 2024.
- Who is it for?
- Macaron fits teams that want typed, colocated styles with no runtime library shipped to the browser, and it is a natural fit for a Solid or React project that already lives in a monorepo with Vite or esbuild. It is the wrong choice for a project that cannot add a build step to its styling, and it is the wrong choice for anyone unwilling to read the compiler's output, because the playground exists precisely because the extraction is not obvious.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 44 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Styles are extracted by the compiler, not by a runtime library
Macaron calls itself compiler-augmented typesafe CSS-in-JS, and the README headline is CSS-in-JS with zero runtime, type safety and colocation. The first feature bullet makes the mechanism concrete: zero runtime bundles with build time style extraction.
That is the whole premise. In most CSS-in-JS setups you import a styling function, it runs in the browser, and it injects rules at runtime, which means shipping a styling engine to users. Macaron inverts that. The style objects you write are ordinary TypeScript values, and a build step pulls them out into static CSS, leaving the component code holding a class name instead of a call.
The playground is the clearest window into how this works. The README says you can try the examples at the macaron playground and see how macaron figures out which expressions have to be extracted and what your code would look like after being transpiled. That is a compiler with a source transform, not a runtime library, and understanding which expressions get extracted is the central learning task for anyone adopting it.
The acknowledgements add one more concrete detail: macaron uses Vanilla Extract for evaluating files. The extraction machinery is not written from scratch, which is why the styling API can mirror vanilla-extract closely.
A styled() API that looks like styled-components but ships nothing
The README states that macaron supports both the styled-components API and a vanilla styling API. The styled API is the familiar one: a styled() call that takes a tag name and a configuration object, producing a component you use like any other React or Solid component.
// or import it from @macaron-css/solid
import { styled } from '@macaron-css/react';
const Button = styled('button', {
base: {
borderRadius: 6,
},
variants: {
color: {
neutral: { background: 'whitesmoke' },
brand: { background: 'blueviolet' },
accent: { background: 'slateblue' },
},
size: {
small: { padding: 12 },
medium: { padding: 16 },
large: { padding: 24 },
},
rounded: {
true: { borderRadius: 999 },
},
},
compoundVariants: [
{
variants: {
color: 'neutral',
size: 'large',
},
style: {
background: 'ghostwhite',
},
},
],
defaultVariants: {
color: 'accent',
size: 'medium',
},
});
// Use it like a regular solidjs/react component
function App() {
return (
<Button color="accent" size="small" rounded>
Click me!
</Button>
);
}The component is then rendered with variant props, which is where the Stitches-like variants API comes in. The README lists a Stitches-like variants API as a feature, and the styling example shows compoundVariants and defaultVariants alongside the variant groups.
The practical difference from styled-components is not the API shape but where the work happens. Same authoring style, and the component ends up as a plain element with a generated class name once the build finishes. The style props you pass at render time are resolved during compilation rather than during rendering, which is the source of both the type safety claim and the zero runtime claim.
Note that the package name follows the framework: the React import comes from @macaron-css/react, and the Solid one from @macaron-css/solid.
Variants, compound variants and default variants resolved statically
The variants system is worth a closer look, because it is the part that most often forces a CSS-in-JS library into a runtime. A styled component with a color variant and a size variant, a compound rule for a specific pair, and a default for each axis is exactly the shape that a runtime library resolves on every render.
Macaron handles it at build time instead. In the README example the color variant group has neutral, brand and accent, the size group has small, medium and large, and a compoundVariants entry sets a background when color is neutral and size is large. defaultVariants then set color to accent and size to medium.
compoundVariants: [
{
variants: {
color: 'neutral',
size: 'large',
},
style: {
background: 'ghostwhite',
},
},
],
defaultVariants: {
color: 'accent',
size: 'medium',
},Because the possible variant values are known statically, the compiler can enumerate the resulting class combinations and emit them as plain CSS rules ahead of time. The application then selects among generated class names.
That also explains why type safety is a headline feature. Variant names and their allowed values are part of the object literal, so a typo in a variant prop is a type error in the consumer's code rather than a silently missing style at runtime. The README lists first class Typescript support as a separate bullet, and the repository topics name vanilla-extract and stitches alongside css and css-in-js.
A vanilla-extract style API you can call inside the same file
The second API is the one that makes macaron more than a styled-components clone. The README says the styling API is the same API as vanilla-extract, but allows styles to be defined in the same file, improving the developer experience. The vanilla-extract docs are linked for the API reference.
Vanilla-extract normally asks you to put styles in a separate .css.ts file. Macaron lets you keep them next to the component, which is the colocation claim. The functions can also be called directly inside expressions, giving a css prop-like API with zero runtime cost:
import { style } from '@macaron-css/core';
function App() {
return (
<div
class={style({
display: 'flex',
padding: '10px',
})}
>
<button class={style({ color: 'red' })}>Click me</button>
</div>
);
}The core import is @macaron-css/core, which is where the style function lives, while the framework-specific packages provide the styled entry point.
The trade-off with colocation is worth naming. Vanilla-extract's separate file is not an accident; it keeps styles out of the component module graph so the build can treat them as static assets. Macaron buys a better editing experience by giving that up, and the README is candid that the playground exists so you can see which expressions get extracted.
React, Solid and Qwik from one monorepo
The feature list says macaron has out of box support for React, Solid and Qwik, and the repository layout backs that up. The root package.json declares a workspace with packages for vite, solid, react, qwik, integration, esbuild, core and babel, alongside example workspaces for vite, react, solid, solid-start and vanilla.
That structure tells you how the project is organised. @macaron-css/core holds the framework independent pieces, the framework packages supply the styled entry point for each renderer, and the integration packages hold the Vite and esbuild plugins. A babel package is present in the workspace as well, and the README does not describe how it is meant to be used.
The examples directory mirrors that: examples/react, examples/solid, examples/solid-start, examples/vanilla and examples/vite. Solid Start in particular matters because it is a meta-framework with its own build pipeline, and having a working example for it is the difference between a supported setup and an unsupported one.
The examples/vanilla directory is there too, which lines up with a goal of supporting use beyond the React ecosystem without pretending the React API is the only entry point.
For an adopter, the practical check is simple: find the package for your framework in packages/, and copy the matching example rather than starting from the README snippets.
Vite and esbuild integrations, and what the build actually runs
Integrations for esbuild and Vite are listed as features, and the packages workspace includes integration, esbuild and vite packages to match. Since macaron is a compile time transform, the integration is the part that matters operationally: it has to run in your bundler's build step for styles to be extracted at all.
The root package.json shows how the project itself is developed and released. It is a private workspace root named macaron-css, with esbuild and esbuild-register as runtime dependencies, and the scripts are short:
"scripts": {
"build": "node -r esbuild-register scripts/build.ts",
"bump": "node -r esbuild-register scripts/bump.ts",
"test": "jest",
"release": "node -r esbuild-register scripts/publish.ts"
},So the build, version bump and publish steps are TypeScript scripts run through esbuild-register, the test suite is jest, and a jest.config.js sits at the root. The repository also carries a scripts/ directory and a site/ directory that holds the documentation site, and a CNAME file at the root.
One caveat for anyone evaluating this: the README does not describe how the plugin is added to a Vite or esbuild configuration, so the setup details live in the online documentation rather than in the repository overview.
Pushed through 2026, last tagged release in October 2024
The maintenance picture needs two dates, and they point in different directions. The last push to the repository was on 2026-09-11, so the source tree is still being worked on. The release tags tell a slower story: v1.6.1 on 2024-10-24, v1.6.0 on 2024-10-23, and v1.5.3 on 2024-04-14.
So there has been no tagged release since October 2024 even though the repository has been pushed to through 2026. If you install macaron from npm you are getting code from the 2024 release line, while the main branch may be ahead of it. Check which one your project consumes before assuming a fix or a feature exists in the published package.
The license is MIT, and both the root package.json and the LICENSE file at the repository root say so. That is permissive enough to use in commercial products without obligations beyond attribution and keeping the license text.
Documentation lives at the macaron.js.org site, with the full reference there, a page describing how the compiler works under docs/working, and the interactive playground mentioned earlier. The README is a short summary by design, so for anything beyond the two API examples, the site and the example projects are where the detail is.
Editorial conclusion
Macaron fits teams that want typed, colocated styles with no runtime library shipped to the browser, and it is a natural fit for a Solid or React project that already lives in a monorepo with Vite or esbuild. It is the wrong choice for a project that cannot add a build step to its styling, and it is the wrong choice for anyone unwilling to read the compiler's output, because the playground exists precisely because the extraction is not obvious. Verify first that the package you need exists in packages/ for your framework, that the Vite or esbuild integration in your version of the toolchain supports the plugin, and that the published v1.6.1 from 2024-10-24 still matches the behaviour you expect, since the repository has been pushed to as recently as 2026-09-11 without a newer tag.
Frequently asked questions
What is macaron and what problem does it solve?
Macaron is a TypeScript-first CSS-in-JS library that extracts styles at build time instead of injecting them at runtime. The README lists zero runtime bundles with build time style extraction, colocation of styles and components, and first class Typescript support.
Which frameworks does macaron support?
The README states out of box support for React, Solid and Qwik, with framework packages under packages/ such as @macaron-css/react, @macaron-css/solid and @macaron-css/core.
How is macaron different from styled-components?
The authoring API is the same style, with a styled() call and a variants configuration, but macaron resolves styles at build time and emits static CSS. styled-components does that work in the browser, which is why macaron describes its bundles as zero runtime.
How does macaron relate to vanilla-extract?
The styling API is the same as vanilla-extract, and macaron uses the vanilla-extract team work for evaluating files, but styles can be defined in the same file as the component instead of a separate stylesheet file.
Does macaron work with Vite and esbuild?
Yes, integrations for esbuild and Vite are listed as features, and the workspace root package.json lists packages for vite, esbuild, integration, core, babel, react, solid and qwik, plus example projects for each.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/macaron-css-macaron)Was this useful?