Outlets

Outlets tell the router where to render content. In the following example the active routes content will be rendered within the Outlet.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::prelude::*;
extern crate dioxus_ssr;

fn Index(cx: Scope) -> Element {
    render! {
        h1 { "Index" }
    }
}

fn App(cx: Scope) -> Element {
    use_router(
        cx,
        &|| RouterConfiguration {
            synchronous: true,
            ..Default::default()
        },
        &|| Segment::content(comp(Index))
    );

    render! {
        header { "header" }
        Outlet { }
        footer { "footer" }
    }
}

let mut vdom = VirtualDom::new(App);
vdom.rebuild();
let html = dioxus_ssr::render(&vdom);
assert_eq!(
    html,
    "<header>header</header><h1>Index</h1><footer>footer</footer>"
);
}

The example above will output the following HTML (line breaks added for readability):

<header>
    header
</header>
<h1>
    Index
</h1>
<footer>
    footer
</footer>

Nested Outlets

When using nested routes, we need to provide equally nested Outlets.

Learn more about nested routes in their own chapter.

Named Outlets

When building complex apps, we often need to display multiple pieces of content simultaneously. For example, we might have a sidebar that changes its content in sync with the main part of the page.

When defining our routes, we can use RouteContentMulti instead of RouteContent::Component (we've been using this through the Into trait) to tell the router about our content.

We then can use a named Outlet in our output, to tell the router where to put the side content.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::prelude::*;
extern crate dioxus_ssr;


fn Main(cx: Scope) -> Element {
    render! {
        main { "Main Content" }
    }
}

struct AsideName;
fn Aside(cx: Scope) -> Element {
    render! {
        aside { "Side Content" }
    }
}

fn App(cx: Scope) -> Element {
    use_router(
        cx,
        &|| RouterConfiguration {
            synchronous: true,
            ..Default::default()
        },
        &|| {
            Segment::content(
                multi(Some(comp(Main)))
                    .add_named::<AsideName>(comp(Aside))
            )
        }
    );

    render! {
            Outlet { }
            Outlet {
                name: Name::of::<AsideName>()
            }
    }
}

let mut vdom = VirtualDom::new(App);
vdom.rebuild();
let html = dioxus_ssr::render(&vdom);
assert_eq!(html, "<main>Main Content</main><aside>Side Content</aside>");
}

The example above will output the following HTML (line breaks added for readability):

<main>
    Main Content
</main>
<aside>
    Side Content
</aside>

Outlet depth override

When nesting Outlets, they communicate with each other. This allows the nested Outlet to render the content of the nested route.

We can override the detected value. Be careful when doing so, it is incredibly easy to create an unterminated recursion. See below for an example of that.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::{history::MemoryHistory, prelude::*};
extern crate dioxus_ssr;

fn RootContent(cx: Scope) -> Element {
    render! {
        h1 { "Root" }
        Outlet { }
    }
}

fn NestedContent(cx: Scope) -> Element {
    render! {
        h2 { "Nested" }
    }
}

fn App(cx: Scope) -> Element {
    use_router(
        cx,
        &|| RouterConfiguration {
            synchronous: true,
            history: Box::new(MemoryHistory::with_initial_path("/root").unwrap()),
            ..Default::default()
        },
        &|| {
            Segment::empty().fixed(
                "root",
                Route::content(comp(RootContent)).nested(
                    Segment::content(comp(NestedContent))
                )
            )
        }
    );

    render! {
        Outlet {
            depth: 1
        }
    }
}

let mut vdom = VirtualDom::new(App);
vdom.rebuild();
let html = dioxus_ssr::render(&vdom);
assert_eq!(html, "<h2>Nested</h2>");
}

The example above will output the following HTML (line breaks added for readability):

<h2>
    Nested
</h2>

Outlet recursion

This code will create a crash due to an unterminated recursion using Outlets.


#![allow(unused)]
fn main() {
// Hidden lines (like this one) make the documentation tests work.
extern crate dioxus;
use dioxus::prelude::*;
extern crate dioxus_router;
use dioxus_router::prelude::*;

fn Content(cx: Scope) -> Element {
    render! {
        h1 { "Heyho!" }
        Outlet {
            depth: 0,
        }
    }
}

fn App(cx: Scope) -> Element {
    use_router(cx, &Default::default, &|| Segment::content(comp(Content)));

    render! {
        Outlet { }
    }
}
}

The Outlet in the App component has no parent Outlet, so its depth will be 0. When rendering for the path /, it therefore will render the Content component.

The Content component will render an h1 and an Outlet. That OUtlet would usually have a depth of 1, since it is a descendant of the Outlet in the App component. However, we override its depth to 0, so it will render the Content component.

That means the Content component will recurse until someone (e.g. the OS) puts a stop to it.