Open-source project
jsxstyle/jsxstyle avatar
jsxstyle/jsxstyle

jsxstyle: inline CSS-in-JSX with nine layout primitives and build-time extraction

Inline style system for JSX

2,111 stars59 forksTypeScriptApache-2.0

At a glance

What is it?
jsxstyle is a CSS-in-JS library for React, Preact, Solid, and Astro that writes styles as component props and converts each unique prop value to a hashed atomic class name. Its README states there are seven components in the library but the table immediately below that sentence lists nine.
Who is it for?
jsxstyle fits React, Preact, Solid, or Astro projects that want styles co-located with component code and can accept class names that look like hashed strings in developer tools. Before adopting, check whether build-time extraction via @jsxstyle/webpack-plugin or @jsxstyle/vite-plugin applies to the build setup, because static styles can be removed from the JavaScript bundle entirely while dynamic ones stay at runtime.
Can I use it commercially?
Yes. Apache-2.0 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 107 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 November 6, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Co-located styles as component props, not a separate stylesheet

Separate stylesheet files create a context switch: editing a component requires opening a second file to change how it looks. jsxstyle addresses this by moving styles directly onto JSX components as props, where a numeric or string value for a CSS property name applies that style to the element.

Naming fatigue is the second problem the README describes. Instead of inventing names like Wrapper, OuterContainer, or CardBody, a developer uses Block, Row, Col, or Grid. Named components remain available through composition, using a type exported from @jsxstyle/core:

tsx
import type { JsxstyleComponentProps } from '@jsxstyle/core';

const RedBlock: React.FC<JsxstyleComponentProps> = (props) => (
  <Block {...props} color="red" />
);

No special API is needed to create a named variant: spreading props and adding a default value is all the README shows. Whether the underlying component is Block, Row, or anything else, the same pattern applies.

The README count is off: nine layout primitives, not seven

The README introduces the component table with the phrase "jsxstyle provides the following seven components," but counting the rows in the table immediately after gives 9, not 7. Block renders with display: block. Inline renders with display: inline. InlineBlock renders with display: inline-block. Row sets display: flex, flex-direction: row, and justify-content: center. Col sets display: flex and flex-direction: column. InlineRow sets display: inline-flex and flex-direction: row. InlineCol sets display: inline-flex and flex-direction: column. Grid sets display: grid. Box carries no default styles.

Box is the blank-slate option, useful when no default layout applies and a starting display value would need to be overridden immediately. For anything that fits the flex or grid patterns, one of the other 8 primitives comes closer to the desired starting point without requiring an override prop.

Picking the framework package and using Row and Block for the first time

jsxstyle publishes framework-specific packages rather than one universal install. For React, the package is @jsxstyle/react; for Preact, @jsxstyle/preact; for Solid, @jsxstyle/solid; for Astro, @jsxstyle/astro. There is no single entry point covering all frameworks, so the choice comes before installation.

Once the correct package is installed, the README shows this layout as a first example:

jsx
<Row padding={15} gap={15}>
  <Block
    component="img"
    src="url(http://graph.facebook.com/justinbieber/picture?type=large)"
    borderRadius={5}
    height={64}
    width={64}
  />
  <Col fontFamily="system-ui" fontSize={16} lineHeight="24px">
    <Block fontWeight={600}>Justin Bieber</Block>
    <Block fontStyle="italic">Canadian</Block>
  </Col>
</Row>

Of all the prop names a jsxstyle component accepts, 3 are not CSS properties and will not touch the style system. Setting component changes the underlying HTML tag, defaulting to 'div' when omitted. The props prop passes values directly to the underlying element without any style processing. Using mediaQueries defines an object of query strings keyed by short identifiers, which are then usable as prop prefixes. Every other prop name is interpreted as a CSS property.

Each unique prop value gets its own hashed class name, reused on repetition

When jsxstyle encounters a CSS prop, it generates a hashed, content-based class name for that specific key-value pair. If two different components both set color='red', both receive the same class name and only one CSS rule is written to the document. Style injection is skipped for any pair already seen.

Class names are intentionally unlike human-written names: the README describes them as hashed and content-based. That distinction matters when reading developer tools. Computed styles will show opaque identifiers rather than anything descriptive, so tracing a style back to its source requires finding the matching prop in JSX rather than reading a class name. For projects that rely on CSS class names for integration tests or for querying in end-to-end tests, this changes how selectors are written.

Pseudoclasses and media queries through prop name prefixes

Pseudoclasses and pseudoelements are applied by prefixing the CSS property name with the pseudoclass or pseudoelement name. A hover color uses hoverColor, and a placeholder color uses placeholderColor. When both a pseudoclass and a pseudoelement are needed on the same prop, the pseudoclass name comes first: hoverPlaceholderColor rather than placeholderHoverColor. 12 pseudoclasses are supported: active, checked, disabled, empty, enabled, focus, hover, invalid, link, required, target, and valid. 4 pseudoelements are supported: placeholder, selection, before, and after.

jsx
<Block
  component="input"
  color="#888"
  activeColor="#333"
  placeholderColor="#BBB"
/>

Media queries use the mediaQueries prop alongside prefixed style props. Define an object of query strings keyed by short identifiers, then prepend those identifiers to any prop that should be scoped to that query. The README is explicit about a constraint: only one media query prefix can be applied per prop at a time.

jsx
<Block
  mediaQueries={{
    sm: 'screen and (max-width: 640px)',
    lg: 'screen and (min-width: 1280px)',
  }}
  color="red"
  smColor="blue"
  lgColor="green"
/>

@jsxstyle/webpack-plugin pulls static styles out of the JavaScript bundle at build time

At runtime, jsxstyle injects CSS rules into the document as components render. @jsxstyle/[email protected], the most recent release at time of writing, changes that model for any style prop whose value is known at build time. During the build, the plugin analyses jsxstyle component code, extracts the static styles, and writes them to a separate CSS file rather than leaving them for the runtime injector. For components whose styling does not depend on runtime variables, the plugin reduces and in some cases entirely eliminates the runtime jsxstyle code.

A Vite equivalent, @jsxstyle/[email protected], covers the same extraction path for Vite-based builds. Neither plugin is required: jsxstyle works without them, with all styles injected at runtime. Dynamic styles, where a prop value comes from a JavaScript expression or state variable, fall through to the runtime path regardless of the plugin. Whether extraction is worth adding depends on how much of a project's styling is static, and that requires auditing the actual component code.

Where the one-prefix rule and the absent theming layer limit adoption

The single media query prefix per prop is a documented constraint, and it affects how responsive components are written. A value that needs to change at two breakpoints, or one that combines a media query with a pseudoclass, requires either splitting into separate props or restructuring the component. For Tailwind CSS users moving to jsxstyle, this is a meaningful difference: Tailwind composes breakpoints and state variants freely, while jsxstyle restricts each prop to one prefix.

No theming layer appears in the README. Projects that need design tokens or shared colour palettes have to build that infrastructure themselves, whether through a React context object, a separate CSS file declaring custom properties, or another mechanism not covered by the library. jsxstyle provides 9 primitive components and the atomic injection; building a coherent design system on top of them is the consuming project's job, not something jsxstyle automates.

Editorial conclusion

jsxstyle fits React, Preact, Solid, or Astro projects that want styles co-located with component code and can accept class names that look like hashed strings in developer tools. Before adopting, check whether build-time extraction via @jsxstyle/webpack-plugin or @jsxstyle/vite-plugin applies to the build setup, because static styles can be removed from the JavaScript bundle entirely while dynamic ones stay at runtime. Projects that need more than one media query prefix per prop, or a built-in theming layer, will need to build that infrastructure on top of the primitives jsxstyle provides.

Frequently asked questions

What is jsxstyle?

jsxstyle is a CSS-in-JS library for React, Preact, Solid, and Astro that writes styles as props on a set of layout components such as Block, Row, and Col. Each unique style prop value becomes a hashed atomic CSS class name injected into the document at render time.

How does jsxstyle differ from Tailwind CSS?

Tailwind CSS uses utility class names written in markup and generates a static CSS file at build time, with full breakpoint and state variant composition per class. jsxstyle uses component props that become hashed class names at runtime, and it restricts each prop to one media query prefix at a time.

Does jsxstyle support TypeScript?

The repository is written in TypeScript and exports a JsxstyleComponentProps type from @jsxstyle/core. Type definitions ship with each framework-specific package.

Can jsxstyle extract styles at build time?

Yes, for static style props. @jsxstyle/webpack-plugin and @jsxstyle/vite-plugin both analyse jsxstyle components during the build and extract static values to a CSS file. Style props whose values are runtime expressions stay injected at runtime.

What frameworks does jsxstyle support?

jsxstyle publishes separate packages for React (@jsxstyle/react), Preact (@jsxstyle/preact), Solid (@jsxstyle/solid), and Astro (@jsxstyle/astro). There is no single universal package.

Official sources

  1. jsxstyle/jsxstyle on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jsxstyle-jsxstyle.svg)](https://hysenlabs.com/projects/jsxstyle-jsxstyle)

Was this useful?