For AI agents: the complete documentation index is at llms.txt. Remove the trailing slash from a page URL and append .md to read its Markdown version. For the docs home, use index.md.

Wrapping React

One of Reflex's most powerful features is the ability to wrap React components and take advantage of the vast ecosystem of React libraries.

If you want a specific component for your app but Reflex doesn't provide it, there's a good chance it's available as a React component. Search for it on npm, and if it's there, you can use it in your Reflex app. You can also create your own local React components and wrap them in Reflex.

For a complete example with typed props, Python state, and an event handler, follow Wrapping React Step by Step.

Simple Example

Simple components that don't have any interaction can be wrapped with just a few lines of code.

Below we show how to wrap the Spline library can be used to create 3D scenes and animations.

The 3D preview loads automatically as you scroll to it. The wrapper uses rx.NoSSRComponent so Spline is imported only when it mounts in the browser.

Start Building Now!

Interactive preview loads as you scroll.

ColorPicker Example

Similar to the Spline example we start with defining the library and tag. In this case the library is react-colorful and the tag is HexColorPicker.

We also have a var color which is the current color of the color picker.

Since this component has interaction we must specify any event triggers that the component takes. The color picker has a single trigger on_change to specify when the color changes. This trigger takes in a single argument color which is the new color.

#db114b

from reflex.components.component import NoSSRComponent


class ColorPicker(NoSSRComponent):
    library = "[email protected]"
    tag = "HexColorPicker"
    color: rx.Var[str]
    on_change: rx.EventHandler[lambda color: [color]]


color_picker = ColorPicker.create


class ColorPickerState(rx.State):
    color: str = "#db114b"

    @rx.event
    def set_color(self, value: str):
        self.color = value


def index():
    return rx.box(
        rx.vstack(
            rx.heading(ColorPickerState.color, as_="h2", color="white"),
            color_picker(on_change=ColorPickerState.set_color),
        ),
        background_color=ColorPickerState.color,
        padding="5em",
        border_radius="1em",
    )
Expand

Naming And Scoping Client State

Whether a client state var is shared app-wide follows from whether you name it:

shared = rx.client_state(
    "", name="search_query"
)  # global: any component, and the backend
private = rx.client_state("")  # scoped to the component tree using it

A named var resolves in one app-wide store, so any component can read and write it and push, retrieve, global_value and global_set all work against it.

An unnamed var gets a compile-time name and is scoped: the outermost page or @rx.memo whose body uses it owns it, so each instance of that component gets its own state, the way React's useState does. Nothing outside the tree can address it, and a page's unnamed vars start again from their defaults each time the page mounts. Name a var to keep its value across pages.

Where you construct the var decides who shares it. Construct it inside a component and each instance gets its own:

@rx.memo
def copy_button(text: rx.Var[str]) -> rx.Component:
    copied = rx.client_state(False)  # one per rendered button
    ...

Construct it once at module level and reference it from several components and they share it, owned by the outermost page or @rx.memo that uses it. This is the same as lifting state up in React. Note the consequence: adding a read at page level makes the page the owner, which collapses per-instance state below it into one shared value. The owner follows from where the var is used, never from which component renders first.

A plain helper function called several times gives each call its own state for free, since the var is constructed once per call:

def counter():
    count = rx.client_state(0)  # a distinct var per call
    return rx.hstack(
        rx.button("-", on_click=count.set(lambda v: v - 1)),
        rx.heading(count.value),
        rx.button("+", on_click=count.set(lambda v: v + 1)),
    )


rx.vstack(counter(), counter(), counter())  # three independent counters

Inside rx.foreach, give the item its own state by making the item function an @rx.memo component; each rendered instance then owns its unnamed vars:

@rx.memo
def row(item: rx.Var[str]) -> rx.Component:
    expanded = rx.client_state(False)  # one per rendered row
    ...


rx.foreach(State.items, lambda item: row(item=item))

Pass prefix= to make generated names readable in the compiled output: rx.client_state(0, prefix="counter").

Setting Client State From Plain JavaScript

value and set are the normal way to use a client state var, but they resolve to a hook, so they only work inside a component that Reflex renders. When you are wrapping a library that hands you a plain JavaScript callback, use global_value and global_set instead. They need no hook, so they work anywhere in your compiled page. Pass them as a prop, so the import they need comes with them:

from typing import Any

picker_color = rx.client_state("#db114b", name="picker_color")


class MyPicker(rx.Component):
    library = "some-non-react-picker"
    tag = "Picker"

    # The library calls this with the picked color.
    on_pick: rx.Var[Any]


# `global_set` is a plain function, so a non-React callback can call it.
MyPicker.create(on_pick=picker_color.global_set)

Do not format them into a string, such as one returned from add_custom_code: the string keeps the expression but drops its import.

Reads through global_value are a point-in-time snapshot with no reactivity, so prefer value inside components. Writes through global_set re-render every component subscribed to that var, exactly like set does. Both require a named client state var, since the name is what identifies the value.

rx.call_script is the one place these do not work: its code is evaluated inside the Reflex runtime module, so your page's imports are not in scope there. Reach the store through the runtime's refs object instead:

rx.call_script('refs["__client_state"].set("picker_color", "#ffffff")')

What Not To Wrap

There are some libraries on npm that are not do not expose React components and therefore are very hard to wrap with Reflex.

A library like spline below is going to be difficult to wrap with Reflex because it does not expose a React component.

import { Application } from '@splinetool/runtime';

// make sure you have a canvas in the body
const canvas = document.getElementById('canvas3d');

// start the application and load the scene
const spline = new Application(canvas);
spline.load('https://prod.spline.design/6Wq1Q7YGyM-iab9i/scene.splinecode');

You should look out for JSX, a syntax extension to JavaScript, which has angle brackets (<h1>Hello, world!</h1>). If you see JSX, it's likely that the library is a React component and can be wrapped with Reflex.

If the library does not expose a react component you need to try and find a JS React wrapper for the library, such as react-spline.

import Spline from "@splinetool/react-spline";

export default function App() {
  return (
    <div>
      <Spline scene="https://prod.spline.design/6Wq1Q7YGyM-iab9i/scene.splinecode" />
    </div>
  );
}

Sharing a Component as a Package

To reuse a wrapped component across apps or publish it to PyPI, start from the component template. It is a GitHub template repository with a wrapped component, tests, a demo app, generated type stubs, and workflows that check every push and publish tagged releases to PyPI with trusted publishing. Select Use this template on GitHub, then run its rename script to give the package your own name.

In the next page, we will go step by step through a more complex example of wrapping a React component.