Type-safe, zero-runtime styled components for OCaml and Reason.

styled-ppx is a ppx  and a library that brings styled components to OCaml and Reason, for both Melange  and native (server-reason-react ). Write plain CSS inside your components and get React components or class names back — parsed, validated and type-checked at compile time.

module Center = [%styled.div {|
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
|}];
 
<Center> {React.string("Hello from the future!")} </Center>
module Center = [%styled.div {|
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
|}]
 
let app = <Center> (React.string "Hello from the future!") </Center>

Type-safe CSS

styled-ppx ships its own CSS parser and type-checker. Every property and value is validated against the CSS spec while you compile: a typo in a property name, an invalid value, or a wrongly-typed interpolation is a compile error, not a broken page. This is a huge time-saver when working with CSS at scale.

Zero runtime: static extraction

Unlike CSS-in-JS libraries, styled-ppx does not generate styles at runtime. All CSS is extracted while your project compiles:

  1. The ppx parses and validates your CSS, splits it into atomic rules (one class per declaration), and hashes each one into a content-addressed class name.
  2. A build step (styled-ppx.generate) collects every rule in your project and writes a single, deduplicated styles.css.
  3. At runtime, your components only carry class names. Dynamic values ($(expr) interpolations) become CSS custom properties supplied through the style attribute.

No style injection, no hashing at runtime, no flash of unstyled content, no double-rendering for SSR. Read more in How it works.

Features

How the ppx fits in

Generally speaking, a ppx is OCaml’s system for macros, similar to babel plugins : a small program that runs before compilation and transforms your source code.

styled-ppx expands four extensions:

ExtensionWhat you get
[%css "..."]a CSS.styles value: class names plus (optionally) inline custom-property values
[%styled.<tag> "..."]a React component for the given HTML tag
[%styled.global "..."]global styles as a React component (@font-face, resets, :root variables)
[%keyframe "..."]a typed @keyframes animation name

If you are curious about the actual transformation, check the snapshot tests .

You can learn more about ppxes in Tarides blog: Introduction to OCaml ppx ecosystem .