Skip to main content

Crate autumn_plugin_react

Crate autumn_plugin_react 

Source
Expand description

React islands for Autumn apps: React 19 components inside Maud + htmx pages.

  1. Build your components with a bundler. The entry file registers them (the README has a version that also checks the global):

    import { createElement, version } from "react";
    import { flushSync } from "react-dom";
    import { createRoot } from "react-dom/client";
    import { Counter } from "./Counter.jsx";
    
    (window.autumnReact ??= []).push({
      createElement, createRoot, flushSync, version, components: { Counter },
    });
  2. Embed the build output as a PluginAssets bundle and install it:

use autumn_plugin_react::{Island, ReactPlugin, react_bundle, react_script};
use autumn_web::assets::PluginAssets;
use autumn_web::prelude::*;

static ISLANDS: PluginAssets = PluginAssets::from_files(
    "app-islands",
    &[("islands.js", b"/* esbuild output */")],
);

#[get("/")]
async fn index() -> AutumnResult<Markup> {
    let counter = Island::new("Counter")
        .props(&serde_json::json!({ "start": 3 }))?
        .fallback(html! { p { "Count: 3" } });
    Ok(html! {
        html {
            head { (react_script()) (react_bundle(&ISLANDS)) }
            body { (counter) }
        }
    })
}

autumn_web::app()
    .plugin(ReactPlugin::new().bundle(&ISLANDS))
    .routes(routes![index])
    .run()
    .await;

The loader (react-islands.js) mounts each island with createRoot. It mounts islands that htmx swaps in and unmounts islands that htmx swaps out. A data-react-props change renders the same root again, so React keeps the state. It uses no inline script and no eval, so it works under the default CSP (script-src 'self').

§Security

The loader mounts each data-react-island element in the page. If your app shows user HTML, the sanitizer must remove data-react-*, hx-* and data-hx-* attributes. data-react-ignore alone is not sufficient: an htmx out-of-band swap can move an element out of it. Do not spread untrusted props onto DOM elements.

Structs§

Island
One React island: an element that the loader mounts a component into.
PropsUpdate
New props for mounted islands, sent in an HX-Trigger header.
ReactPlugin
Installs React islands in an Autumn app.

Enums§

AttrError
The error from Island::attr.
JsonKind
The kind of a JSON value that is not an object.
MountWhen
When the loader mounts an island.
PropsError
The error from Island::props.

Constants§

ASSETS_NAMESPACE
URL namespace of the loader bundle: /static/_plugins/react/.
LOADER_JS
Logical path of the loader in REACT_ASSETS.
PLUGIN_NAME
The crate name. ReactPlugin with no app bundles uses it as its plugin name. Each ReactPlugin declares it in its contract.
PROPS_EVENT
The DOM event that the loader reads new props from.

Statics§

REACT_ASSETS
The loader bundle. It holds one file, LOADER_JS.

Functions§

react_bundle
Renders the tags of an app bundle, in this order:
react_script
Renders the <script defer> tag of the island loader.