Skip to content

Markerless placeholders/Portals/Multiwindow/const builder/optimized templates#5554

Open
ealmloff wants to merge 366 commits into
DioxusLabs:mainfrom
ealmloff:fix-textarea-hydration
Open

Markerless placeholders/Portals/Multiwindow/const builder/optimized templates#5554
ealmloff wants to merge 366 commits into
DioxusLabs:mainfrom
ealmloff:fix-textarea-hydration

Conversation

@ealmloff

@ealmloff ealmloff commented May 12, 2026

Copy link
Copy Markdown
Member

Changes

  • Removes all markers and placeholders from fullstack and core
  • Templates are represented at the core level instead of leaking into renderers
  • Templates are reworked into a flat structure that is easier to work with in const code
  • RSX lowers to a const builder api which fully supports templates
  • Html elements are fully trait based and take the exact same path for elements and components
  • You can define html elements in user code
  • Portals/multirenderer support via a renderer id + routing

Placeholderless

Currently we use comment nodes in several places at runtime and in hydration:

  1. To represent Placeholder nodes at runtime
  2. To represent empty lists
  3. To surround text nodes for hydration

This PR removes all of those in favor of keeping track of the position of nodes relative to an anchor. This lets us:

  1. Properly render element that interpret children as text like textarea (Fixes Textarea with placeholder child renders <!--placeholder8--> when first loaded in fullstack #5548)
  2. Drop the different between ssr prerendering and rendering since all of that data is now interpreted at runtime

Templates and const builders

This PR introduces a const builder api for vnodes which builds up a cons list-style template in the type system at the same time as the dynamic values. Since the builder only builds up deep trees of &'static Node, we can keep the work to condense the template into a more runtime usable flat form into a single method where &mut ConstVec is usable. This helps to keep down compile times

#[component]
fn Dashboard(#[props(into)] title: String, metrics: &'static [Metric]) -> Element {
    Ok(html::main()
        .onclick(|event| println!("{event:?}"))
        .class("dashboard")
        .child((
            Header.builder().title(title).total(metrics.len()).build(),
            html::section().class("metric-grid").child(
                metrics
                    .iter()
                    .copied()
                    .map(|metric| MetricCard.builder().metric(metric).build()),
            ),
            html::footer()
                .class("note")
                .child("Created with typed HTML builders and component props builders."),
        ))
        .into_vnode())
}

Html extensions

Html elements are no longer namespaced in dioxus_elements and are now trait based which makes them easy to define from user code. We expose a macro to define them which works for the builder api rsx now expands to:

use dioxus::prelude::*;

dioxus::html::define_elements! {
    #[element(name = "analytics-panel")]
    analyticsPanel {
        metric,
        #[attr(name = "data-region")]
        region,
    }
}

fn main() {
    dioxus::launch(app);
}

fn app() -> Element {
    let selected_metric = "conversion-rate";

    rsx! {
        analyticsPanel {
            class: "metric-card",
            metric: selected_metric,
            region: "north-america",
            h2 { "Revenue" }
            p { "Custom elements can still use regular HTML children." }
        }
    }
}

Warning

Since html elements are no longer name spaced, there are some breaking changes around what items can be in scope when you use an element. Eg. you need to use html::main {} instead of main {} to differentiate between the main function in your code and the main element builder in html

Portal support

Core now supports a render id which scopes elementids and events to a specific renderer. This makes it possible to support many desktop windows in the same runtime with shared context, and signals.

TODO:

  • Round trip fuzzing with a mock renderer using mutatis
  • Make sure the docsite renders correctly
  • Make sure the component library renders correctly
  • Make sure hotreloading still works
  • Playwright tests passing
  • Update most tests to use an oracle renderer to assert the output structure is correct instead of asserting the ops themselves

Stacked on #5571, #5633, #5634

Closes #628
Closes #5548
Closes #4019
Closes #5181
Closes #2467
Closes #1627
Closes #2040

@ealmloff ealmloff requested a review from a team as a code owner May 12, 2026 19:48
@ealmloff ealmloff marked this pull request as draft May 12, 2026 19:48
@ealmloff ealmloff changed the title Markerless placeholders and hydration Markerless placeholders/Cooperative scheduling (fibers)/Portals/Multiwindow May 20, 2026
@ealmloff ealmloff added breaking This is a breaking change core relating to the core implementation of the virtualdom desktop Suggestions related to the desktop renderer labels May 21, 2026
@ealmloff ealmloff force-pushed the fix-textarea-hydration branch from 54cf23a to c64b2a0 Compare May 22, 2026 18:27
@ealmloff ealmloff changed the title Markerless placeholders/Cooperative scheduling (fibers)/Portals/Multiwindow Markerless placeholders/Portals/Multiwindow Jun 8, 2026
ealmloff and others added 21 commits June 12, 2026 15:24
Co-Authored-By: Evan Almloff <evanalmloff@gmail.com>
Co-Authored-By: Evan Almloff <evanalmloff@gmail.com>
Co-Authored-By: Evan Almloff <evanalmloff@gmail.com>
Replace the type-erased AnyProps (BoxedAnyProps / Box<dyn AnyProps>)
approach with a trait-based RenderDriver abstraction for component
lifecycle dispatch.

Key changes:
- New render_driver.rs: RenderDriver trait + BodyDriver (plain
  components) + DynWriter (sized wrapper bridging dyn WriteMutations
  into the generic diff pipeline)
- VComponent now holds Rc<dyn RenderDriver> instead of BoxedAnyProps
  + render_fn
- ScopeState no longer stores props directly; the driver owns them
- scope_arena: new_scope() takes an Rc<dyn RenderDriver>;
  run_scope_with() replaces the old props-based run_scope()
- diff/component.rs: create/diff/remove dispatch through the driver
- suspense/component.rs: SuspenseDriver implements RenderDriver for
  suspense boundaries
- Deleted any_props.rs

Co-Authored-By: Evan Almloff <evanalmloff@gmail.com>
Comment thread packages/core/src/scope_arena.rs
@ealmloff ealmloff marked this pull request as ready for review June 25, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking This is a breaking change core relating to the core implementation of the virtualdom desktop Suggestions related to the desktop renderer

Projects

None yet

2 participants