| Crates.io | leptos-posthoc |
| lib.rs | leptos-posthoc |
| version | 0.1.0 |
| created_at | 2025-06-04 09:57:15.351151+00 |
| updated_at | 2025-06-04 09:57:15.351151+00 |
| description | A crate for dynamically hydrating static/opaque HTML using leptos components |
| homepage | |
| repository | https://github.com/FlexiFormal/leptos-posthoc |
| max_upload_size | |
| id | 1700066 |
| size | 114,790 |
Allows for "hydrating" an existent DOM with reactive leptos components, without the entire DOM having to be generated by leptos components.
<insert your favourite fancy feature here>. For an example, see the examples/csr directory:
the index.html has a node <script src='csr_example.js'></script>, which "hydrates" nodes with the
data-replace-with-leptos-attribute with leptos components that add a hover-popup (using
thaw).examples/ssr directory.Say we want to replace all elements with the attribute data-replace-with-leptos with a leptos component
MyReplacementComponent, that simply wraps the original children in a div with a solid red border. This
component would roughly look like this:
#[component]
fn MyReplacementComponent(orig:OriginalNode) -> impl IntoView {
view! {
<div style="border: 1px solid red;">
<DomChildren orig />
</div>
}
}
This component takes an orig:[OriginalNode] that represents the, well, original [Element].
So, where do we get orig from?
If we already have an e:&[Element], we can simply call e.into().
More likely, we don't have an [Element] yet. Moreover, we probably want to iterate over the entire body
once to find all nodes we want to make reactive, and we also need to set up a global reactive system for all
our inserted components.
To do that, we call [hydrate_body] (requires the csr feature flag) with a function that takes the
[OriginalNode] of the body and returns some leptos view; e.g.:
#[component]
fn MainBody(orig:OriginalNode) -> impl IntoView {
// set up some signals, provide context etc.
view!{
<DomChildren orig/>
}
}
#[wasm_bindgen(start)]
pub fn run() {
console_error_panic_hook::set_once();
hydrate_body(|orig| view!(<MainBody orig/>).into_any())
}
This sets up the reactive system, but does not yet replace any elements further down in the DOM. To do that,
we provide a function that takes an &[Element] and optionally returns an
[FnOnce]() -> impl [IntoView]+'static, if the element should be changed. This function is then passed to
[DomChildrenCont], which will iterate over all children of the replaced element and replace them with the
provided function.
Let's modify our MainBody to replace all elements with the attribute data-replace-with-leptos with a
MyReplacementComponent:
fn replace(e:&Element) -> Option<impl FnOnce() -> AnyView> {
e.get_attribute("data-replace-with-leptos").map(|_| {
let orig: OriginalNode = e.clone().into();
|| view!(<MyReplacementComponent orig/>).into_any()
})
}
#[component]
fn MainBody(orig:OriginalNode) -> impl IntoView {
// set up some signals, provide context etc.
view!{
<DomChildrenCont orig cont=replace/>
}
}
#[component]
fn MyReplacementComponent(orig:OriginalNode) -> impl IntoView {
view! {
<div style="border: 1px solid red;">
<DomChildrenCont orig cont=replace/>
</div>
}
}
...now, replace will get called on every element of the DOM, including those that were "moved around" in
earlier MyReplacementComponents, respecting the proper reactive graph (regardin signal inheritance etc.).
In general, for SSR we can simply use the normal leptos components to generate the entire DOM. We control the server, hence we control the DOM anyway.
However, it might occasionally be the case that we want to dynamically extend the DOM at some point by
retrieving HTML from elsewhere, and then want to do a similar "hydration" iteration over the freshly inserted
nodes. This is what [DomStringCont] is for, and it does not require the csr feature:
#[component]
fn MyComponentThatGetsAStringFromSomewhere() -> impl IntoView {
// get some HTML string from somewhere
// e.g. some API call
let html = "<div data-replace-with-leptos>...</div>".to_string();
view! {
<DomStringCont html cont=replace/>
}
}
See the examples/ssr directory for a full example.