Building a Nest

In this chapter, we will begin to build the blog portion of our site which willinclude links, nested routes, and route parameters.

Our site visitors won't know all the available pages and blogs on our site so weshould provide a navigation bar for them. Our navbar will be a list of links going between our pages.

We want our navbar component to be rendered on several different pages on our site. Instead of duplicating the code, we can create a component that wraps all children routes. This is called a layout component. To tell the router where to render the child routes, we use the [Outlet] component.

Let's create a new NavBar component:

#[component]
fn NavBar(cx: Scope) -> Element {
    render! {
        nav {
            ul {
                li { "links" }
            }
        }
        // The Outlet component will render child routes (In this case just the Home component) inside the Outlet component
        Outlet::<Route> {}
    }
}

Next, let's add our NavBar component as a layout to our Route enum:

#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
    // All routes under the NavBar layout will be rendered inside of the NavBar Outlet
    #[layout(NavBar)]
        #[route("/")]
        Home {},
    #[end_layout]
    #[route("/:..route")]
    PageNotFound { route: Vec<String> },
}

To add links to our NavBar, we could always use an HTML anchor element but that has two issues:

  1. It causes a full-page reload
  2. We can accidentally link to a page that doesn't exist

Instead, we want to use the Link component provided by Dioxus Router.

The Link is similar to a regular <a> tag. It takes a target and children.

Unlike a regular <a> tag, we can pass in our Route enum as the target. Because we annotated our routes with the [route(path)] attribute, the Link will know how to generate the correct URL. If we use the Route enum, the rust compiler will prevent us from linking to a page that doesn't exist.

Let's add our links:

#[component]
fn NavBar(cx: Scope) -> Element {
    render! {
        nav {
            ul {
                li {
                    Link {
                        // The Link component will navigate to the route specified
                        // in the target prop which is checked to exist at compile time
                        to: Route::Home {},
                        "Home"
                    }
                }
            }
        }
        Outlet::<Route> {}
    }
}

Using this method, the Link component only works for links within ourapplication. To learn more about navigation targets seehere.

Now you should see a list of links near the top of your page. Click on one andyou should seamlessly travel between pages.

URL Parameters and Nested Routes

Many websites such as GitHub put parameters in their URL. For example,https://github.com/DioxusLabs utilizes the text after the domain todynamically search and display content about an organization.

We want to store our blogs in a database and load them as needed. We alsowant our users to be able to send people a link to a specific blog post.Instead of listing all of the blog titles at compile time, we can make a dynamic route.

We could utilize a search page that loads a blog when clicked but then our userswon't be able to share our blogs easily. This is where URL parameters come in.

The path to our blog will look like /blog/myBlogPage, myBlogPage being theURL parameter.

First, let's create a layout component (similar to the navbar) that wraps the blog content. This allows us to add a heading that tells the user they are on the blog.

#[component]
fn Blog(cx: Scope) -> Element {
    render! {
        h1 { "Blog" }
        Outlet::<Route> {}
    }
}

Now we'll create another index component, that'll be displayed when no blog postis selected:

#[component]
fn BlogList(cx: Scope) -> Element {
    render! {
        h2 { "Choose a post" }
        ul {
            li {
                Link {
                    to: Route::BlogPost { name: "Blog post 1".into() },
                    "Read the first blog post"
                }
            }
            li {
                Link {
                    to: Route::BlogPost { name: "Blog post 2".into() },
                    "Read the second blog post"
                }
            }
        }
    }
}

We also need to create a component that displays an actual blog post. This component will accept the URL parameters as props:

// The name prop comes from the /:name route segment
#[component]
fn BlogPost(cx: Scope, name: String) -> Element {
    render! {
        h2 { "Blog Post: {name}"}
    }
}

Finally, let's tell our router about those components:

#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
    #[layout(NavBar)]
        #[route("/")]
        Home {},
        #[nest("/blog")]
            #[layout(Blog)]
            #[route("/")]
            BlogList {},
            #[route("/post/:name")]
            BlogPost { name: String },
            #[end_layout]
        #[end_nest]
    #[end_layout]
    #[route("/:..route")]
    PageNotFound {
        route: Vec<String>,
    },
}

That's it! If you head to /blog/1 you should see our sample post.

Conclusion

In this chapter, we utilized Dioxus Router's Link, and Route Parameterfunctionality to build the blog portion of our application. In the next chapter,we will go over how navigation targets (like the one we passed to our links)work.