Full Guide
Let's walk step by step through how to wrap a React component in Reflex, using the color picker as our primary example. You can also see the full API reference.
#db114b
Find The Component
There are two ways to find a component to wrap:
- Write the component yourself locally.
- Find a well-maintained React library on npm that contains the component you need.
In both cases, the process of wrapping the component is the same except for the library
field.
In this guide we are wrapping the HexColorPicker
component from the react-colorful library.
Define the Component
The first step to wrapping any React component is to subclass rx.Component
.
Set the Library and Tag
The library is just the name of the npm
package, and the tag is the name of the React component from the package that you want to wrap. Some packages have multiple components, and you can wrap each one as a separate Reflex component.
You can generally find the library and tag by looking at the import statement in the React code.
In this case, the library is react-colorful
and the tag is HexColorPicker
.
When you create your component, Reflex will automatically install the library for you.
Local Components
You can also wrap components that you have written yourself. Local components should be stored in your assets
directory. For example, you could define a basic Hello
component like this:
Then specify the library as following (note: we use the public
directory here instead of assets
as this is the directory that is served by the web server):
Local Packages
If the component is part of a local package, available on Github, or
downloadable via a web URL, it can also be wrapped in Reflex. Specify the path
or URL after an @
following the package name.
Any local paths are relative to the .web
folder, so you can use ../
prefix
to reference the Reflex project root.
Some examples of valid specifiers for a package called
@masenf/hello-react
are:
- GitHub:
@masenf/hello-react@github:masenf/hello-react
- URL:
@masenf/hello-react@https://github.com/masenf/hello-react/archive/refs/heads/main.tar.gz
- Local Archive:
@masenf/hello-react@../hello-react.tgz
- Local Directory:
@masenf/hello-react@../hello-react
It is important that the package name matches the name in package.json
so
Reflex can generate the correct import statement in the generated javascript
code.
These package specifiers can be used for library
or lib_dependencies
.
Counter
Although more complicated, this approach is useful when the local components have additional dependencies or build steps required to prepare the component for use.
Some important notes regarding this approach:
- The repo or archive must contain a
package.json
file. prepare
orbuild
scripts will NOT be executed. The distribution archive, directory, or repo must already contain the built javascript files (this is common).
Import Types
Sometimes the component is a default export from the module (meaning it doesn't require curly braces in the import statement).
In these cases you must set is_default = True
in your component class, as we did in the Spline example in the overview section:
Library Dependencies
By default Reflex will install the library you have specified in the library property. However, sometimes you may need to install other libraries to use a component. In this case you can use the lib_dependencies
property to specify other libraries to install.
As seen in the Spline example in the overview section, we need to import the @splinetool/runtime
library to use the Spline
component. We can specify this in our component class like this:
In this example we are adding this dependency to pin its version. It would be automatically installed alongside the react-spline
library when the component is created.
A useful time to add lib_dependencies
is when we are wrapping a component and we want plugins or extensions for the library. A good example is the React component react-markdown
with its extensions.
Versions
You can specify the version of the library you want to install by appending @
and the version number to the library name.
This is recommended to ensure that your app works consistently across different environments.
Dynamic Imports
Some libraries you may want to wrap may require dynamic imports. This is because they they may not be compatible with Server-Side Rendering (SSR).
To handle this in Reflex, subclass NoSSRComponent
when defining your component.
Often times when you see an import something like this:
You can wrap it in Reflex like this, here we are wrapping the react-plotly.js
library which requires dynamic imports:
It may not always be clear when a library requires dynamic imports. A few things to keep in mind are if the component is very client side heavy i.e. the view and structure depends on things that are fetched at run time, or if it uses window
or document
objects directly it will need to be wrapped as a NoSSRComponent
.
Some examples are:
- Video and Audio Players
- Maps
- Drawing Canvas
- 3D Graphics
- QR Scanners
- Reactflow
The reason for this is that it does not make sense for your server to render these components as the server does not have access to your camera, it cannot draw on your canvas or render a video from a file.
In addition, if in the component documentation it mentions nextJS compatibility or server side rendering compatibility, it is a good sign that it requires dynamic imports.
Additional Imports
Sometimes you may need to import additional files or stylesheets to use a component. You can do this by overriding the add_imports
method in your component class. The method returns a dictionary where the key is the library and the values are a list of imports.
You can use the empty string as the key to import files that are not part of a library.
Aliases
If you are wrapping another component with the same tag as a component in your project you can use aliases to differentiate between them and avoid naming conflicts.
Lets check out the code below, in this case if we needed to wrap another color picker library with the same tag we use an alias to avoid a conflict.
Custom Code
Sometimes you may need to add custom code to your component, such as definining constants and functions used. Custom code will be inserted outside of the react component function.
To add custom code to your component you can use the add_custom_code
method in your component class.
Props
Props are the variables that you can pass to the component. In the case of our color picker, we have a single prop color
. Props are defined using rx.Var
with the type of the prop. Specifying the type helps the compiler catch errors and provides better intellisense.
Then when you create the component, you can pass in the props as keyword arguments.
Default Value
You can set a default value for the prop by assigning it in the class definition.
Serializers
Vars can be any type that can be serialized to JSON. This includes primitive types like strings, numbers, and booleans, as well as more complex types like lists, dictionaries, and dataframes.
In case you need to serialize a more complex type, you can use the serializer
decorator to convert the type to a primitive type that can be stored in the state. Just define a method that takes the complex type as an argument and returns a primitive type. We use type annotations to determine the type that you want to serialize.
For example, the Plotly component serializes a plotly figure into a JSON string that can be stored in the state.
We can then define a var of this type as a prop in our component.
Event Handlers
Recall that event handlers are ways that components can handle user interactions. In the case of the color picker, we have a single event trigger on_change
that triggers when the color changes. The event trigger takes a single argument color
which is the new color.
We can then bind this event trigger to an event handler in our state that takes the color as an argument.
Assets
Experimental feature added in v0.5.3.
If a wrapped component depends on assets such as images, scripts, or
stylesheets, these can kept adjacent to the component code and
included in the final build using the rx._x.asset
function.
rx._x.asset
returns a relative path that references the asset in the compiled
output. The target files are copied into a subdirectory of assets/external
based on the module where they are initially used. This allows third-party
components to have external assets with the same name without conflicting
with each other.
For example, if there is an SVG file named wave.svg
in the same directory as
this component, it can be rendered using rx.image
and rx._x.asset
.
Debugging
If you encounter an error while wrapping a component it is recommended to check the Console in the browser developer tools. You can access this by going to inspect element and then clicking on the Console tab on Mac. This is because the Console is where most Javascript errors are logged.