Native

This guide assumes that you’ll use opam and dune and have a server or native process running.

Install

opam install styled-ppx

Packages available

  • styled-ppx is the ppx to transform [%styled.div ""] and [%cx ""]
  • styled-ppx.native is the library with the CSS bindings, and the implementation of emotion.sh on the server. Capable of storing CSS, hashing it and generating a unique classnames, autoprefixing, etc.

Usage

Add styled-ppx under dune’s preprocess pps for any library or executable. Add styled-ppx.native as libraries.

(library
  (name ...)
  (libraries
+  styled-ppx.native
   server-reason-react)
  (preprocess
   (pps
+   styled-ppx
    melange.ppx
    server-reason-react-ppx)))
(melange.emit
  (target ...)
  (libraries
+  styled-ppx.native
   server-reason-react)
  (preprocess
   (pps
+   styled-ppx
    melange.ppx
    server-reason-react-ppx)))

Note: server-reason-react and server-reason-react-ppx are optional, and only needed if you use styled components ([%styled.div {||}]).

API

  • CSS.get_stylesheet returns a string with all styles
  • CSS.style_tag returns a <style /> React element, with all styles. This is designed to be used with server-reason-react.

Example

let className = [%cx {|
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  align-items: center
|}];

let stylesheet: string = CSS.get_stylesheet();

print_endline(className);
/* .css-1xuw4bg */

print_endline(stylesheet);
/*
  .css-1xuw4bg {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
  } */

Example with server-reason-react

/* This is a server-reason-react module with those styles encoded as a unique className */
module Link = [%styled.a (~color=CSS.hex("4299E1")) => {|
  font-size: 1.875rem;
  line-height: 1.5;
  text-decoration: none;
  margin: 0px;
  padding: 10px 0px;
  color: $(color);
|}];

/* This is a unique className pointing to those styles */
let layout = [%cx {|
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  align-items: center
|}];

/* Later in a component */
let app =
  <div className=layout>
    <Link
      color={CSS.hex("333333")}
      href="https://sancho.dev"
      rel="noopener noreferrer">
      {React.string("sancho.dev")}
    </Link>
  </div>;

To generate the CSS on the server, you would need to use <CSS.style_tag />. This component will render a <style> with all generated stylesheet.

Given a Page component to simulate a real-world scenario:

module Page = {
  [@react.component]
  let make = () => {
    <html>
      <head>
        <CSS.style_tag />
      </head>
    </html>
  }
}

If you’re using dynamic CSS values, such as dynamic-components or interpolation based on runtime values. You would need to evaluate your app before extracting all the CSS.

module Component = {
  [@react.component]
  let make = (~value) => {
    let className = switch (value) {
      | Some(value) => [%cx "margin: $(value)"]
      | None => [%cx "margin: 0"]
    };
    <div className />
  }
};

This component needs to be rendered before the CSS.style_tag otherwise the [%cx] calls won’t be added to the stylesheet, since they aren’t being executed yet.

To solve this, we recommend: render the React application first, and then inject the result as dangerouslySetInnerHTML.

/* `App` here is the entry component of your React application, while the `Document` component is only meant to run on the server. */
module Document = {
  [@react.component]
  let make = () => {
    let app = ReactDOM.renderToString(<App />);

    <html>
      <head>
        <CSS.style_tag />
      </head>
      <body>
        <div id="#root" dangerouslySetInnerHTML={"__html": app} />
      </body>
    </html>
  }
};

/* Let's asume we are running [dream](https://github.com/aantron/dream) */
let some_server_side_handler = _request => {
  /* Here we render the entire HTML */
  Dream.html(ReactDOM.renderToString(<Document />));
};

Advanced

If you don’t want to render the stylesheet directly, you can use CSS.get_stylesheet to obtain the stylesheet as a string. In this case, the hydration with the client won’t be supported.

To make sure hydration works, you would need the following <style/> tag

React.createElement "style"
  [
    Bool ("data-s", true);
    String ("data-emotion", "css " ^ CSS.get_string_style_hashes ());
    DangerouslyInnerHtml (CSS.get_stylesheet ());
  ]
  []