postcss-js lets PostCSS plugins read your style objects
PostCSS for React Inline Styles, Free Style and other CSS-in-JS
At a glance
- What is it?
- postcss-js is a MIT licensed parser and objectifier that presents a JavaScript style object as a PostCSS Root, which is what makes it possible to run Autoprefixer, RTLCSS or Stylelint over inline styles, and its design is a bridge in both directions rather than a new styling system.
- Who is it for?
- postcss-js fits a codebase that has adopted a CSS-in-JS style but still wants the PostCSS toolchain, such as autoprefixer, RTLCSS or a linting rule, to apply to the inline styles, and it fits a build that wants to compile style objects to static CSS. It does not fit a project that has decided to move away from CSS-in-JS, since a runtime CSS-in-JS library needs none of this.
- 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 7 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A bridge between style objects and PostCSS
postcss-js is PostCSS for CSS-in-JS and for styles in JS objects, and its stated use is running tools like Stylelint or RTLCSS plugins in a workflow that otherwise would not see those styles.
The problem it solves is narrow and real. PostCSS operates on a CSS AST, and a CSS-in-JS codebase has no CSS file to hand it. The styles live in JavaScript objects, so the usual PostCSS plugins, which know how to add vendor prefixes or rewrite a direction, simply never run. A team that wants those plugins on inline styles has to either convert the styles to CSS first by hand, or give the plugins a way to read JavaScript.
That is what this package is. It provides a parser that turns a style object into a PostCSS Root, and an objectifier that goes the other way, turning a Root back into a style object. The PostCSS processor then runs over that Root as usual.
The credit line in the README is worth noting for a support question: the PostCSS JS Parser is built by Evil Martians, a consultancy for developer tools, AI and cybersecurity startups, and the package sits in the postcss organisation with Andrey Sitnik as the author. The PostCSS logo in the README is the philosopher's stone, which is the project's usual mark.
The direction of the bridge is what makes it flexible. Because both directions exist, the same package can be used to post-process an existing style object, to compile a style object to CSS, or to convert a CSS file into a style object.
Four entry points and a sync or async processor
The API is small and the package exports each piece separately, so an application pulls in only what it uses. The exports map in the package metadata lists the root entry plus ./async, ./objectifier, ./parser, ./process-result and ./sync, each with its own CommonJS and ES module files.
The two processors are the main thing. sync takes a list of plugins and creates a PostCSS processor with a simple API, but with only synchronous PostCSS plugins supported. The result is just a function that takes one style object and returns another, which is why the usage example is two lines: create the processor with the plugins, then call it with a style.
const postcssJs = require('postcss-js')
const autoprefixer = require('autoprefixer')
const prefixer = postcssJs.sync([autoprefixer])
const style = prefixer({
userSelect: 'none'
})async is the same thing with asynchronous plugins added, and the returned processor returns a Promise instead of a value.
The split is the practical decision. A synchronous processor is far easier to use in a build script or a test, and many PostCSS plugins are synchronous anyway, but a plugin that reads a file or talks to a network service is not. Choosing sync and then adding an async plugin is the mistake that shows up as a processor that quietly ignores the plugin.
parse() converts numbers to pixels and understands Free Style
The parse function takes a CSS-in-JS style object and returns a PostCSS Root instance. Two conversions happen inside it, and both are load-bearing for the object format.
The first is that numbers are converted to pixels, so top: 10 becomes top: 10px, which is what makes a bare number in a style object mean what it looks like it means. The second is that it parses Free Style style selectors and at-rules, so a nested object such as a media query with a nested hover selector becomes a proper PostCSS structure with an at-rule and a rule inside it.
{
'@media screen': {
'&:hover': {
top: 10
}
}
}The method uses the Custom Syntax name convention, which means it can be plugged into the normal PostCSS process call:
postcss().process(obj, { parser: postcssJs })That is the important detail for integration. Rather than a bespoke pipeline, the parser is registered as a PostCSS syntax, so every plugin, and the whole of the PostCSS ecosystem, works against it without knowing anything about JavaScript.
The reverse of parse is objectify, which takes a PostCSS Root and converts it to a CSS-in-JS style object. The usage example shows what that means in practice: a CSS string with a custom property and a media query comes back as an object with a custom property key and a nested at-rule key, and the declaration names are converted to camelCase, so z-index becomes zIndex.
The two directions, shown as a compile
The README demonstrates the CSS-in-JS to CSS direction with the plain PostCSS API, and the output shows exactly what the parser is doing to the structure.
const postcss = require('postcss')
const postcssJs = require('postcss-js')
const style = {
'top': 10,
'&:hover': {
top: 5
}
}
postcss()
.process(style, { parser: postcssJs })
.then(result => {
result.css //=> top: 10px;
// &:hover { top: 5px; }
})Note what happens to the nested hover object. It becomes a rule with a nested selector rather than a flat declaration list, which is the CSS-in-JS convention surviving into the AST. A plugin that rewrites declarations, such as a prefixer, sees ordinary declarations inside it and behaves as expected.
The other direction is the objectifier, and the example converts a CSS string with a custom property and a media query into an object:
const postcss = require('postcss')
const postcssJs = require('postcss-js')
const css =
'--text-color: #DD3A0A; @media screen { z-index: 1; color: var(--text-color) }'
const root = postcss.parse(css)
postcssJs.objectify(root) //=> {
// '--text-color': '#DD3A0A',
// '@media screen': {
// zIndex: '1',
// color: 'var(--text-color)'
// }
// }The camelCase conversion is visible in zIndex, and the at-rule becomes a key with a nested object. That is a genuinely useful direction for a project that wants its design tokens or theme objects to live in CSS and be consumed as JavaScript.
The webpack json-loader trap and other friction
The troubleshooting section is short, and the problem it documents will cost an afternoon if you meet it unprepared.
Autoprefixer and some other plugins need a json-loader to import data, so the loader has to be installed and added to the webpack configuration:
loaders: [
{
test: /\.json$/,
loader: 'json-loader'
}
]The note above it is that webpack may need some extra config for some PostCSS plugins, so this is a category of problem rather than a single bug. The reason is not in the README, but it is the usual one: a plugin that reads a browser data file such as a browserslist target or a caniuse database ends up importing JSON, and the bundler needs a loader for that.
That is the sharp edge of running PostCSS inside a bundler, and it is the sort of thing worth knowing before you commit to the approach rather than after the first failed build.
The other friction is the peer dependency. The package requires postcss ^8.4.21 as a peer, not as a regular dependency, which is the correct arrangement, but it means the PostCSS version is your project's decision and not something this package can pin for you.
The alternative, and what the package deliberately is not
The realistic alternative is a runtime CSS-in-JS library, where styles are generated as CSS when the component renders, and the toolchain runs over the generated stylesheet or not at all.
The difference in approach is when the work happens. A runtime library produces CSS during rendering, so the styles exist only in the browser and a build-time plugin has nothing to process unless the library offers its own extraction step. postcss-js is the opposite: the style object is data, and the plugin operates on that data at build time, with the output either another style object or static CSS.
That is what makes the pairing with autoprefixer sensible. Vendor prefixes are decided at build time, once, and the result is committed to the style object or written to a stylesheet. The cost of that approach is the extra step in the pipeline, and the coupling to a PostCSS AST, which is what the json-loader issue above is really about.
The other alternative is to do nothing: keep the styles as CSS modules or a plain stylesheet and let the PostCSS pipeline run on it as usual. That is simpler, and it is the right answer for a team that has not adopted CSS-in-JS. postcss-js exists for the team that has, and would rather not give up the plugin ecosystem to get there.
MIT terms, a strict Node floor, and a coverage gate
The licence is MIT, with the LICENSE file at the repository root, and the package is published as postcss-js at version 5.1.0, authored by Andrey Sitnik.
The engines field is strict: node ^20, ^22 or 24 and above. The peer dependency is postcss ^8.4.21, and the development dependency pins postcss ^8.5.6. So a project on Node 18 is out, and the PostCSS version in use is the one that decides whether the plugin ecosystem you want works.
The tests are run with uvu, and the coverage tool is c8, configured with lines at 100 and check-coverage turned on. A hundred per cent line coverage enforced in the build is a strong statement about how small and how well tested this package is, and it is consistent with the file layout: the source is split into parser.js, objectifier.js, sync.js, async.js and process-result.js, each with a matching declaration file and the root also shipping an index.d.ts and an index.d.mts for the ES module build.
The funding configuration lists Open Collective for the PostCSS project and GitHub Sponsors for the author, and the repository uses pnpm with a pnpm-lock.yaml, plus a devcontainer.json and a .editorconfig.
On maintenance, the release history is regular: 5.1.0 on 2026-02-13, 5.0.3 on 2025-12-21 and 5.0.2 on 2025-12-08, with a CHANGELOG.md at the root. The repository is not archived and the last push was on 2026-10-16, so the work is current even though the newest published version is from the start of the year.
Editorial conclusion
postcss-js fits a codebase that has adopted a CSS-in-JS style but still wants the PostCSS toolchain, such as autoprefixer, RTLCSS or a linting rule, to apply to the inline styles, and it fits a build that wants to compile style objects to static CSS. It does not fit a project that has decided to move away from CSS-in-JS, since a runtime CSS-in-JS library needs none of this. Before adopting it, check that the plugins you want to use are synchronous if you use the sync processor, remember that the webpack json-loader requirement applies to Autoprefixer and similar plugins, and pin the postcss peer dependency to a version your project already uses, since the package requires postcss ^8.4.21.
Frequently asked questions
What does postcss-js do exactly?
It provides a PostCSS parser that turns a CSS-in-JS style object into a PostCSS Root instance, converting numbers to pixels and parsing Free Style style selectors and at-rules, and an objectifier that converts a Root back into a style object with camelCase property names.
How do I run Autoprefixer on inline styles?
Create a processor with postcssJs.sync([autoprefixer]) and pass the style object to it, or use the async variant. The sync processor only supports synchronous PostCSS plugins, while the async processor also supports asynchronous ones and returns a Promise.
Why does Autoprefixer fail to build with webpack?
The README documents that Autoprefixer and some other plugins need a json-loader to import data, so the loader has to be installed and added to the webpack configuration with a rule matching .json files.
What are the Node and PostCSS requirements for postcss-js?
The engines field requires Node ^20, ^22 or 24 and above, and postcss is a peer dependency at ^8.4.21, so the PostCSS version is chosen by the consuming project.
Is postcss-js still maintained?
The repository is not archived and the last push was on 2026-10-16. The recent releases are 5.1.0 on 2026-02-13, 5.0.3 on 2025-12-21 and 5.0.2 on 2025-12-08, and the package is MIT licensed.
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/postcss-postcss-js)Was this useful?