update the router guide

This commit is contained in:
Evan Almloff committed 2023-06-01 13:13:59 -05:00
1 parent 67992f7da9
commit 0d3ea377ce
63 files changed
+615 -1705

No files matched your search

+1 -23
View File
@@ -2,30 +2,8 @@
[Introduction](./index.md)
# Features
- [Adding the Router to Your Application](./features/index.md)
- [Defining Routes](./features/routes/index.md)
- [Nested Routes](./features/routes/nested.md)
- [Catch All Routes](./features/routes/catch_all.md)
- [Matching Routes](./features/routes/matching.md)
- [Fallback Routes (404 page)](./features/routes/fallback.md)
- [Multiple Components & Redirects](./features/routes/multiple-and-redirect.md)
- [Outlets](./features/outlets.md)
- [Links & Navigation](./features/navigation/index.md)
- [Named Navigation](./features/navigation/name.md)
- [External Navigation](./features/navigation/external.md)
- [Programmatic Navigation](./features/navigation/programmatic.md)
- [Query](./features/query.md)
- [Navigation Failures](./features/failures/index.md)
- [Named Navigation Failure](./features/failures/named.md)
- [External Navigation Failure](./features/failures/external.md)
- [Redirection Limit Failure](./features/failures/redirection-limit.md)
- [History Providers](./features/history-providers.md)
- [History Buttons](./features/history-buttons.md)
- [Sitemap Generation](./features/sitemap-generation.md)
- [Routing Update Callback](./features/routing-update-callback.md)
# Example Project
- [Overview](./example/index.md)
- [Creating Our First Route](./example/first-route.md)
- [Building a Nest](./example/building-a-nest.md)
+48 -211
View File
@@ -1,137 +1,60 @@
# Building a Nest
Not a bird's nest! A nest of routes!
In this chapter we will begin to build the blog portion of our site which will
include links, nested URLs, and URL parameters. We will also explore the use
case of rendering components directly in the component calling [`use_router`].
In this chapter, we will begin to build the blog portion of our site which will
include links, nested routes, and route parameters.
## Site Navigation
Our site visitors won't know all the available pages and blogs on our site so we
should provide a navigation bar for them.
should 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:
```rust,no_run
# // 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 NavBar(cx: Scope) -> Element {
render! {
nav {
ul { }
}
}
}
```rust, no_run
{{#include ../../examples/nested_routes.rs:nav}}
```
Our navbar will be a list of links going between our pages. We could always use
an HTML anchor element but that would cause our page to reload unnecessarily.
Instead we want to use the [`Link`] component provided by Dioxus Router.
Next, let's add our `NavBar` component as a layout to our Route enum:
The [`Link`] is similar to a regular `a` tag. It takes a target (for now a path,
more on other targets later) and an element. Let's add our links
```rust, no_run
{{#include ../../examples/nested_routes.rs:router}}
```
```rust,no_run
# // 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 NavBar(cx: Scope) -> Element {
render! {
nav {
ul {
// new stuff starts here
li { Link {
target: NavigationTarget::Internal(String::from("/")),
"Home"
} }
li { Link {
target: "/blog", // short form
"Blog"
} }
// new stuff ends here
}
}
}
}
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:
```rust, no_run
{{#include ../../examples/links.rs:nav}}
```
> Using this method, the [`Link`] component only works for links within our
> application. To learn more about navigation targets see
> [here](./navigation-targets.md).
And finally, we add the navbar component in our app component:
```rust,no_run
# // 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 Home(cx: Scope) -> Element { unimplemented!() }
# fn NavBar(cx: Scope) -> Element { unimplemented!() }
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
#
fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration::default(),
&|| Segment::content(comp(Home)).fallback(comp(PageNotFound))
);
render! {
NavBar { } // this is new
Outlet { }
}
}
```
Now you should see a list of links near the top of your page. Click on one and
you should seamlessly travel between pages.
### Active Link Styling
You might want to style links differently, when their page is currently open.
To achieve this, we can tell the [`Link`] to give its internal `a` tag a class
in that case.
```rust,no_run
# // 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 NavBar(cx: Scope) -> Element {
render! {
nav {
ul {
li { Link {
target: NavigationTarget::Internal(String::from("/")),
active_class: "active", // this is new
"Home"
} }
li { Link {
target: "/blog",
active_class: "active", // this is new
"Blog"
} }
}
}
}
}
```
> This will not be reflected in the [full example code](./full-code.md).
## 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 to
dynamically search and display content about an organization.
We want to store our blogs in a database and load them as needed. This'll help
prevent our app from being bloated therefor providing faster load times. We also
We want to store our blogs in a database and load them as needed. We also
want 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 users
won't be able to share our blogs easily. This is where URL parameters come in.
@@ -139,124 +62,38 @@ won'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 the
URL parameter.
First, lets create a component that wraps around all blog content. This allows
us to add a heading that tells the user they are on the blog.
```rust,no_run
# // 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 Blog(cx: Scope) -> Element {
render! {
h1 { "Blog" }
Outlet {}
}
}
```
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.
> Note the `Outlet { }` component. For the components of a nested route to be
> rendered, we need an equally nested outlet. For more details, see the
> [nested routes](../features/routes/nested.md) chapter of the features section.
```rust, no_run
{{#include ../../examples/dynamic_route.rs:blog}}
```
Now we'll create another index component, that'll be displayed when no blog post
is selected:
```rust,no_run
# // 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 BlogList(cx: Scope) -> Element {
render! {
h2 { "Choose a post" }
ul {
li { Link {
target: "/blog/1",
"Read the first blog post"
} }
li { Link {
target: "/blog/2",
"Read the second blog post"
} }
}
}
}
```rust, no_run
{{#include ../../examples/dynamic_route.rs:blog_list}}
```
We also need to create a component that displays an actual blog post. Within
this component we can use the `use_route` hook to gain access to our URL
parameters:
```rust,no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
# extern crate dioxus_router;
# use dioxus_router::prelude::*;
#
struct PostId;
We also need to create a component that displays an actual blog post. This component will accept the URL parameters as props:
fn BlogPost(cx: Scope) -> Element {
let route = use_route(cx).unwrap();
let post_id = route.parameter::<PostId>();
let post = post_id
.map(|id| id.to_string())
.unwrap_or(String::from("unknown"));
render! {
h2 { "Blog Post: {post}"}
}
}
```rust, no_run
{{#include ../../examples/dynamic_route.rs:blog_post}}
```
Finally, let's tell our router about those components.
```rust,no_run
# // 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 Blog(cx: Scope) -> Element { unimplemented!() }
# fn BlogList(cx: Scope) -> Element { unimplemented!() }
# struct PostId;
# fn BlogPost(cx: Scope) -> Element { unimplemented!() }
# fn Home(cx: Scope) -> Element { unimplemented!() }
# fn NavBar(cx: Scope) -> Element { unimplemented!() }
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
#
fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration::default(),
&|| {
Segment::content(comp(Home))
// new stuff starts here
.fixed("blog", Route::content(comp(Blog)).nested(
Segment::content(comp(BlogList))
.catch_all((comp(BlogPost), PostId { }))
))
// new stuff ends here
.fallback(comp(PageNotFound))
}
);
Finally, let's tell our router about those components:
render! {
NavBar { }
Outlet { }
}
}
```rust, no_run
{{#include ../../examples/dynamic_route.rs:router}}
```
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, URL Parameter, and `use_route`
In this chapter, we utilized Dioxus Router's Link, and Route Parameter
functionality 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.
[`Link`]: https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html
[`use_router`]: https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html
[`Link`]: https://docs.rs/dioxus-router/latest/dioxus_router/prelude/fn.GenericLink<R>.html
-1
View File
@@ -1 +0,0 @@
+25 -86
View File
@@ -1,123 +1,62 @@
# Creating Our First Route
In this chapter, we will start utilizing Dioxus Router and add a homepage and a
404 page to our project.
## Fundamentals
Dioxus Router works based on a [`use_router`] hook, a route definition in pure
rust and [`Outlet`] components. If you've ever used [Vue Router], you should
feel right at home with Dioxus Router.
First we need an actual page to route to! Let's add a homepage component:
```rust,no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
#
fn Home(cx: Scope) -> Element {
render! {
h1 { "Welcome to the Dioxus Blog!" }
}
}
The core of the Dioxus Router is the [`Routable`] macro and the [`Router`] component.
First, we need an actual page to route to! Let's add a homepage component:
```rust, no_run
{{#include ../../examples/first_route.rs:home}}
```
## To Route or Not to Route
We want to use Dioxus Router to separate our application into different "pages".
Dioxus Router will then determine which page to render based on the URL path.
To start using Dioxus Router, we need to use the [`use_router`] hook. All other
hooks and components the router provides can only be used as a descendant of a
component calling [`use_router`].
To start using Dioxus Router, we need to use the [`Routable`] macro.
The [`use_router`] hook takes three arguments:
1. `cx`, which is a common argument for all hooks.
2. A [`RouterConfiguration`], which allows us to modify its behavior.
3. A definition of all routes the application contains, in the form of its root
[`Segment`].
The [`Routable`] macro takes an enum with all of the possible routes in our application. Each variant of the enum represents a route and must be annotated with the [`route(path)`] attribute.
```rust,no_run
# // 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 Home(cx: Scope) -> Element { unimplemented!() }
fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration::default(),
&|| Segment::content(comp(Home))
);
render! {
Outlet { }
}
}
```rust, no_run
{{#include ../../examples/first_route.rs:router}}
```
All other hooks and components the router provides can only be used as a descendant of a [`Router`] component.
If you head to your application's browser tab, you should now see the text
`Welcome to Dioxus Blog!` when on the root URL (`http://localhost:8080/`). If
you enter a different path for the URL, nothing should be displayed.
This is because we told Dioxus Router to render the `Home` component only when
the URL path is `/`. The _index_ (`Segment::content()`) functionality we used
basically emulates how web servers treat `index.html` files.
the URL path is `/`.
## What if a Route Doesn't Exist?
In our example Dioxus Router doesn't render anything. Many sites also have a
"404" page for when a URL path leads to nowhere. Dioxus Router can do this too!
In our example, when a route doesn't exist Dioxus Router doesn't render anything. Many sites also have a "404" page when a path does not exist. Let's add one to our site.
First, we create a new `PageNotFound` component.
```rust,no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
#
fn PageNotFound(cx: Scope) -> Element {
render! {
h1 { "Page not found" }
p { "We are terribly sorry, but the page you requested doesn't exist." }
}
}
```rust, no_run
{{#include ../../examples/catch_all.rs:fallback}}
```
Now to tell Dioxus Router to render our new component when no route exists.
```rust,no_run
# // 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 Home(cx: Scope) -> Element { unimplemented!() }
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
#
fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration::default(),
&|| {
Segment::content(comp(Home))
.fallback(comp(PageNotFound)) // this is new
}
);
Next, register the route in the Route enum to match if all other routes fail.
render! {
Outlet { }
}
}
```rust, no_run
{{#include ../../examples/catch_all.rs:router}}
```
Now when you go to a route that doesn't exist, you should see the page not found
text.
## Conclusion
In this chapter we learned how to create a route and tell Dioxus Router what
In this chapter, we learned how to create a route and tell Dioxus Router what
component to render when the URL path is `/`. We also created a 404 page to
handle when a route doesn't exist. Next, we'll create the blog portion of our
site. We will utilize nested routes and URL parameters.
[`Outlet`]: https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html
[`RouterConfiguration`]: https://docs.rs/dioxus-router/latest/dioxus_router/hooks/struct.RouterConfiguration.html
[`Segment`]: https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html
[`use_router`]: https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html
[Vue Router]: https://router.vuejs.org/
+2 -6
View File
@@ -1,9 +1,5 @@
# Full Code
```rust,no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# extern crate dioxus_router;
# extern crate dioxus_web;
{{#include example.rs}}
```rust, no_run
{{#include ../../examples/full_example.rs}}
```
+15 -149
View File
@@ -1,161 +1,27 @@
# Navigation Targets
In the previous chapter we learned how to create links to pages within our app.
We told them where to go using the `target` property. This property takes a
[`NavigationTarget`].
In the previous chapter, we learned how to create links to pages within our app.
We told them where to go using the `target` property. This property takes something that can be converted to a [`NavigationTarget`].
## What is a navigation target?
A [`NavigationTarget`] is similar to the `href` of an HTML anchor element.It
tells the router where to navigate to. The Dioxus Router knows three kinds of
A [`NavigationTarget`] is similar to the `href` of an HTML anchor element. It
tells the router where to navigate to. The Dioxus Router knows two kinds of
navigation targets:
- [`Internal`]: we already saw that. It's basically an `href`, but cannot
link to content outside our app.
- [`External`]: This works exactly like an HTML anchors `href`. In fact,
it is just passed through. Don't use this for in-app navigation as it'll
trigger a page reload by the browser.
- [`Named`]: this is the most interesting form of navigation target. We'll look
at it in detail in this chapter.
- [`Internal`]: We used internal links in the previous chapter. It's a link to a page within our
app represented as a Route enum.
- [`External`]: This works exactly like an HTML anchors' `href`. Don't use this for in-app
navigation as it will trigger a page reload by the browser.
## External navigation
If we need a link to an external page we can do it like this:
```rust,no_run
# // 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 GoToDioxus(cx: Scope) -> Element {
render! {
Link {
target: NavigationTarget::External("https://dioxuslabs.com".into()),
"Explicit ExternalTarget target"
}
Link {
target: "https://dioxuslabs.com", // short form
"Implicit ExternalTarget target"
}
}
}
```rust, no_run
{{#include ../../examples/external_link.rs:component}}
```
> Note that we can use a `str`, just like with [`Internal`]s. The router will
> convert a `str` to an [`External`] if the URL is absolute.
## Named navigation
When defining our routes, we can optionally give them unique static names. This
is required for a feature we call named navigation.
Up to now, when creating links we told the router the exact path to go to. With
named navigation we instead give it a name, and let it figure out the path.
This has several advantages:
- We don't have to remember absolute paths or care about what the current path
is.
- Changing paths later on won't break internal links.
- Paths can easily be localized without affecting app logic.
- The compiler makes sure we don't have typos.
Let's try that now! First, we give our blog post route a name. We can reuse our
`BlogPost` component as a name.
```rust,no_run
# // 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 Blog(cx: Scope) -> Element { unimplemented!() }
# fn BlogList(cx: Scope) -> Element { unimplemented!() }
# struct PostId;
# fn BlogPost(cx: Scope) -> Element { unimplemented!() }
# fn Home(cx: Scope) -> Element { unimplemented!() }
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
#
struct BlogPostName;
fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration::default(),
&|| {
Segment::content(comp(Home))
.fixed("blog", Route::content(comp(Blog)).nested(
Segment::content(comp(BlogList)).catch_all(
ParameterRoute::content::<PostId>(comp(BlogPost))
.name::<BlogPostName>() // this is new
)
))
.fallback(comp(PageNotFound))
}
);
// ...
# unimplemented!()
}
```
Now we can change the targets of the links in our `BlogList` component.
```rust,no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
# extern crate dioxus_router;
# use dioxus_router::prelude::*;
# struct PostId;
# struct BlogPostName;
# fn BlogPost(cx: Scope) -> Element { unimplemented!() }
#
fn BlogList(cx: Scope) -> Element {
render! {
h2 { "Choose a post" }
ul {
li { Link {
target: named::<BlogPostName>().parameter::<PostId>("1"),
"Read the first blog post"
} }
li { Link {
target: named::<BlogPostName>()
.parameter::<PostId>("1")
.query("query"),
"Read the second blog post"
} }
}
}
}
```
As you can see, a [`Named`] requires three fields:
1. the name to navigate to
2. a `Vec` containing all parameters that need to be inserted into the path
3. optionally a query string to use.
### The special root index name
Whether we define any names or not, the router always knows about the
[`RootIndex`] name. Navigating to it tells the router to go to `/`.
We can change the link in our `NavBar` component to take advantage of that.
```rust,no_run
# // 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 NavBar(cx: Scope) -> Element {
render! {
nav {
ul {
li { Link { target: named::<RootIndex>(), "Home" } }
li { Link { target: "/blog", "Blog" } }
}
}
}
}
```
[`External`]: https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External
[`Internal`]: https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal
[`Named`]: https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Named
[`NavigationTarget`]: https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html
[`RootIndex`]: https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.RootIndex.html
@@ -1,65 +1,39 @@
# Redirection Perfection
You're well on your way to becoming a routing master!
In this chapter we will cover utilizing redirects so you can take Rickrolling to
the next level.
In this chapter, we will cover creating redirects
## Creating Redirects
## What Is This Redirect Thing?
A redirect is very simple. When dioxus encounters a redirect while finding out
what components to render, it will redirect the user to the target of the
redirect.
As a simple example, let's say you want user to still land on your blog, even
if they used the path `/myblog`.
if they used the path `/myblog` or `/myblog/:name`.
All we need to do is update our route definition in our app component:
```rust,no_run
# // 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 Blog(cx: Scope) -> Element { unimplemented!() }
# fn BlogList(cx: Scope) -> Element { unimplemented!() }
# struct PostId;
# struct BlogPostName;
# fn BlogPost(cx: Scope) -> Element { unimplemented!() }
# fn Home(cx: Scope) -> Element { unimplemented!() }
# fn NavBar(cx: Scope) -> Element { unimplemented!() }
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
# fn App(cx: Scope) -> Element {
use_router(
cx,
&|| RouterConfiguration::default(),
&|| {
Segment::content(comp(Home))
.fixed("blog", Route::content(comp(Blog)).nested(
Segment::content(comp(BlogList)).catch_all(
ParameterRoute::content::<PostId>(comp(BlogPost))
.name::<BlogPostName>()
)
))
.fixed("myblog", "/blog") // this is new
.fallback(comp(PageNotFound))
}
);
# unimplemented!()
# }
Redirects are special attributes in the router enum that accept a route and a closure
with the route parameters. The closure should return a route to redirect to.
Let's add a redirect to our router enum:
```rust, no_run
{{#include ../../examples/full_example.rs:router}}
```
That's it! Now your users will be redirected to the blog.
Notice that the `"/blog"` `str` is a [navigation target](./navigation-targets.md).
We could also use external or named targets.
### Conclusion
Well done! You've completed the Dioxus Router guide book. You've built a small
Well done! You've completed the Dioxus Router guide. You've built a small
application and learned about the many things you can do with Dioxus Router.
To continue your journey, you can find a list of challenges down below, or you
To continue your journey, you attempt a challenge listed below, look at the [router examples](https://github.com/DioxusLabs/dioxus/tree/master/packages/router/examples), or
can check out the [API reference](https://docs.rs/dioxus-router/).
### Challenges
- Organize your components into seperate files for better maintainability.
- Organize your components into separate files for better maintainability.
- Give your app some style if you haven't already.
- Build an about page so your visitors know who you are.
- Add a user system that uses URL parameters.
@@ -0,0 +1 @@
# External Navigation Failure
@@ -0,0 +1 @@
# Navigation Failures
@@ -0,0 +1 @@
# Named Navigation Failure
@@ -0,0 +1 @@
# Redirection Limit Failure
@@ -0,0 +1 @@
# History Buttons
@@ -0,0 +1 @@
# History Providers
+1
View File
@@ -0,0 +1 @@
# Adding the Router to Your Application
@@ -0,0 +1 @@
# External Navigation
@@ -0,0 +1 @@
# Links & Navigation
@@ -0,0 +1 @@
# Named Navigation
@@ -0,0 +1 @@
# Programmatic Navigation
+1
View File
@@ -0,0 +1 @@
# Outlets
+1
View File
@@ -0,0 +1 @@
# Query
@@ -0,0 +1 @@
# Catch All Routes
@@ -0,0 +1 @@
# Fallback Routes (404 page)
+1
View File
@@ -0,0 +1 @@
# Defining Routes
@@ -0,0 +1 @@
# Matching Routes
@@ -0,0 +1 @@
# Multiple Components & Redirects
@@ -0,0 +1 @@
# Nested Routes
@@ -0,0 +1 @@
# Routing Update Callback
@@ -0,0 +1 @@
# Sitemap Generation
+1 -1
View File
@@ -34,7 +34,7 @@ You can override it by setting the `failure_external_navigation` value of the
[`RouterConfiguration`]. The external URL will be provided via the
[`FailureExternalNavigation`] parameter.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
+1 -1
View File
@@ -14,7 +14,7 @@ forward buttons:
> If you want to navigate through the history programmatically, take a look at
> [`programmatic navigation`](./navigation/programmatic.md).
```rust, no_run
```rust, no_run, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -15,7 +15,7 @@ By default the router uses the [`MemoryHistory`]. It might be changed to use
You can override the default history:
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
+1 -1
View File
@@ -11,7 +11,7 @@ In most cases we want to add the router to the root component of our app. This
way, we can ensure that we have access to all its functionality everywhere. We
add it by using the [`use_router`] hook
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -15,7 +15,8 @@ the [`Link`] component is more convenient to use, as it automatically sets the
`rel` attribute for the link, when the target is external.
## Code Example
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -9,6 +9,7 @@ like this:
```
However, we cannot do that when using the router for two reasons:
1. Anchor tags make the browser load a new page from the server. This takes a
lot of time, and it is much faster to let the router handle the navigation
client-side.
@@ -17,7 +18,8 @@ However, we cannot do that when using the router for two reasons:
To solve these problems, the router provides us with a [`Link`] component we can
use like this:
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -40,6 +42,7 @@ fn SomeComponent(cx: Scope) -> Element {
The `target` in the example above is similar to the `href` of a regular anchor
element. However, it tells the router more about what kind of navigation it
should perform:
- The example uses [`Internal`]. We give it an arbitrary path that will be
merged with the current URL.
- [`Named`] allows us to navigate within our app using predefined names.
+5 -2
View File
@@ -11,6 +11,7 @@ automatically create the actual path to navigate to, even inserting required
parameters.
_Named_ navigation has a few advantages over _path-based_ navigation:
- Links can be created without knowing the actual path.
- It is much easier to find all links to a specific route.
- The router knows what links are invalid (and will panic in debug builds).
@@ -27,7 +28,8 @@ _Named_ navigation has a few advantages over _path-based_ navigation:
> certain conditions. None of these names can be used for app defined routes.
## Code Example
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -90,11 +92,12 @@ fn App(cx: Scope) -> Element {
```
## Check if a name is present
You can check if a specific name is present for the current route. This works
similar to getting the value of a [parameter route](../routes/parameter.md) and
the same restrictions apply.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -4,10 +4,11 @@ Sometimes we want our application to navigate to another page without having the
user click on a link. This is called programmatic navigation.
## Acquiring a [`Navigator`]
To use programmatic navigation, we first have to acquire a [`Navigator`]. For
that purpose we can use the [`use_navigate`] hook.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -23,7 +24,9 @@ fn Content(cx: Scope) -> Element {
```
## Triggering a Navigation
We can use the [`Navigator`] to trigger four different kinds of navigation:
- `push` will navigate to the target. It works like a regular anchor tag.
- `replace` works like `push`, except that it replaces the current history entry
instead of adding a new one. This means the prior page cannot be restored with
@@ -32,7 +35,7 @@ We can use the [`Navigator`] to trigger four different kinds of navigation:
- `Go forward` works like the browsers forward button (the opposite of the back
button).
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -64,6 +67,7 @@ You might have noticed that, like [`Link`], the [`Navigator`]s `push` and
[`Internal`], [`Named`] and [`External`].
## External Navigation Targets
Unlike a [`Link`], the [`Navigator`] cannot rely on the browser (or webview) to
handle navigation to external targets via a generated anchor element.
+17 -22
View File
@@ -3,7 +3,7 @@
[`Outlet`]s tell the router where to render content. In the following example
the active routes content will be rendered within the [`Outlet`].
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -45,24 +45,21 @@ fn App(cx: Scope) -> Element {
The example above will output the following HTML (line breaks added for
readability):
```html
<header>
header
</header>
<h1>
Index
</h1>
<footer>
footer
</footer>
<header>header</header>
<h1>Index</h1>
<footer>footer</footer>
```
## Nested Outlets
When using nested routes, we need to provide equally nested [`Outlet`]s.
> Learn more about [nested routes](./routes/nested.md) 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.
@@ -74,7 +71,7 @@ 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.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -127,23 +124,21 @@ fn App(cx: Scope) -> Element {
The example above will output the following HTML (line breaks added for
readability):
```html
<main>
Main Content
</main>
<aside>
Side Content
</aside>
<main>Main Content</main>
<aside>Side Content</aside>
```
## Outlet depth override
When nesting [`Outlet`]s, 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.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -197,17 +192,17 @@ fn App(cx: Scope) -> Element {
The example above will output the following HTML (line breaks added for
readability):
```html
<h2>
Nested
</h2>
<h2>Nested</h2>
```
### Outlet recursion
This code will create a crash due to an unterminated recursion using
[`Outlet`]s.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
+7 -3
View File
@@ -4,11 +4,12 @@ Some apps use the query part of the URL to encode information. The router allows
you to easily access the query, as well as set it when navigating.
## Accessing the query
The [`use_route`] hook allows us to access the current query in two ways. The
returned `struct` contains a `query` field, that contains the query (without the
leading `?`).
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -26,13 +27,15 @@ fn SomeComponent(cx: Scope) -> Element {
```
## Setting the query
When navigating we can tell the router to change the query. However, the method
we use to do this is very different, depending on how we specify our target.
### [`Internal`] and [`External`]
When using [`Internal`] or [`External`] we have to append our query manually.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -54,10 +57,11 @@ fn SomeComponent(cx: Scope) -> Element {
```
### [`Named`]
When using [named navigation](./navigation/name.md) we can pass the query via
a function.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -10,6 +10,7 @@ functionality with catch all routes.
> The parameter will be URL decoded.
## Creating a content component
We start by creating a component that uses the parameters value.
We can get the current state of the router using the [`use_route`] hook. From
@@ -23,7 +24,7 @@ will later also define on our route.
> The [`use_route`] hook can only be used in components nested within a
> component that called [`use_router`].
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -45,6 +46,7 @@ fn Greeting(cx: Scope) -> Element {
```
## Defining the routes
Now we can define our route. Unlike a fixed [`Route`], a [`ParameterRoute`]
needs two arguments to be created.
@@ -54,7 +56,7 @@ needs two arguments to be created.
> For that reason, the example below would not work in practice, but showing
> both forms (explicit and short) is more important for this example.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -83,6 +85,7 @@ fn App(cx: Scope) -> Element {
```
## Interaction with other routes
Each individual [`Segment`] can only ever have one active route. This means that
when a [`Segment`] has more than just a catch all route, the router has to
decide which is active. It does that this way:
@@ -105,11 +108,13 @@ by the path, i.e. `//`.
>
> If you absolutely need an empty parameter on the root [`Segment`], a URL like
> this _could_ work:
>
> - `https://your-site.example//` for web sites
> - `dioxus://index.html//` for desktop apps
## Full Code
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
+4 -2
View File
@@ -8,10 +8,11 @@ Fallback routes allow us to do that.
> path, like web apps running in the browser.
## A single global fallback
To catch all cases of invalid paths within our app, we can simply add a fallback
route to our root [`Segment`].
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -59,6 +60,7 @@ fn App(cx: Scope) -> Element {
```
## More specific fallback routes
In some cases we might want to show different fallback content depending on what
section of our app the user is in.
@@ -73,7 +75,7 @@ will then replace the global fallback whenever our [`Segment`] was active.
Note the `.clear_fallback(false)` part. If we didn't add this, the fallback
content would be rendered inside the `Settings` component.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
+8 -4
View File
@@ -4,10 +4,11 @@ When creating a router we need to pass it a [`Segment`]. It tells the router
about all the routes of our app.
## Example content
To get a good understanding of how we define routes we first need to prepare
some example content, so we can see the routing in action.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -28,6 +29,7 @@ fn Other(cx: Scope) -> Element {
```
## Index routes
The easiest thing to do is to define an index route.
Index routes act very similar to `index.html` files in most web servers. They
@@ -36,7 +38,7 @@ are active, when we don't specify a route.
> Note that we wrap our `Index` component with [`comp`]. This is because of
> rust type system requirements.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -59,6 +61,7 @@ fn App(cx: Scope) -> Element {
```
## Fixed routes
It is almost as easy to define a fixed route.
Fixed routes work similar to how web servers treat files. They are active, when
@@ -66,7 +69,7 @@ specified in the path. In the example, the path must be `/other`.
> The path will be URL decoded before checking if it matches our route.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -91,7 +94,8 @@ fn App(cx: Scope) -> Element {
```
## Full Code
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
+4 -2
View File
@@ -14,10 +14,11 @@ _Matching_ routes make it easy to implement such behavior.
> unfit for all other purposes.
## Code Example
> Notice that the parameter of a _matching route_ has the same type as a
> [_catch all route_](./catch_all.md).
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -112,6 +113,7 @@ fn App(cx: Scope) -> Element {
```
## Matcher
In the example above, both _matching routes_ use a regular expression to specify
when they match. However, _matching routes_ are not limited to those. They
accept all types that implement the [`Matcher`] trait.
@@ -119,7 +121,7 @@ accept all types that implement the [`Matcher`] trait.
For example, you could (but probably shouldn't) implement a matcher, that
matches all values with an even number of characters:
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus_router;
# use dioxus_router::prelude::*;
@@ -1,11 +1,13 @@
# Multiple Components & Redirects
## Multiple Components
When creating complex apps we sometimes want to have multiple pieces of content
side by side. The router allows us to do this. For more details see the section
about [named `Outlet`s](../outlets.md#named-outlets).
## Redirects
In some cases we may want to redirect our users to another page whenever they
open a specific path. We can tell the router to do this when defining our
routes.
@@ -16,7 +18,7 @@ routes.
In the following example we will redirect everybody from `/` and `/start` to
`/home`.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
+9 -4
View File
@@ -22,6 +22,7 @@ We might want to map this structure to these paths and components:
Nested routes allow us to do this.
## Route Depth
With nesting routes, the router manages content on multiple levels. In our
example, when the path is `/settings`, there are two levels of content:
@@ -34,13 +35,14 @@ content of nested routes to actually be rendered, we also need nested
[`Outlet`]s.
## Defining the content components
We start by creating the components we want the router to render.
Take a look at the `Settings` component. When it gets rendered by an [`Outlet`],
it will render a second [`Outlet`]. Thus the second [`Outlet`] is nested within
the first one, and will in turn render our nested content.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -74,13 +76,14 @@ fn PrivacySettings(cx: Scope) -> Element {
```
## Defining the root [`Segment`]
Now we create the [`Segment`] that we will pass to the router.
Note that we wrap `comp(Settings)` within a [`Route`]. For this exact code that
is unnecessary, as this would be done automatically. However, in the next step
we'll use a method of [`Route`], so we might as well add this now.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -103,6 +106,7 @@ fn App(cx: Scope) -> Element {
```
## Defining the nested [`Segment`]
In order to create nested routes we need to create a nested [`Segment`]. We then
pass it to the [`Route`] on the root segment.
@@ -110,7 +114,7 @@ pass it to the [`Route`] on the root segment.
>
> https://router.example/`root_segment`/`first_nested_segment`/`second_nested_segment`/...
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -143,7 +147,8 @@ fn App(cx: Scope) -> Element {
```
## Full Code
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -4,6 +4,7 @@ In some cases we might want to run custom code when the current route changes.
For this reason, the [`RouterConfiguration`] exposes an `on_update` field.
## How does the callback behave?
The `on_update` is called whenever the current routing information changes. It
is called after the router updated its internal state, but before depended
components and hooks are updated.
@@ -19,7 +20,8 @@ initiated the navigation, was found as a redirect target or returned by the
`on_update` itself.
## Code Example
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# extern crate dioxus_router;
@@ -5,8 +5,10 @@ generating all pages), Dioxus Router provides functions to extract that
information from a [`Segment`].
## Preparing an app
We will start by preparing an app with some routes like we normally would.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
use dioxus::prelude::*;
@@ -74,10 +76,11 @@ fn App(cx: Scope) -> Element {
```
## Modifying the app to make using sitemaps easier
Preparing our app for sitemap generation is quite easy. We just need to extract
our segment definition into its own function.
```rust,no_run
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -117,13 +120,14 @@ fn prepare_routes() -> Segment<Component> {
```
## Sitemaps with parameter names
The first variant to generate sitemaps is very simple. It finds all routes
within the [`Segment`] and adds them to the returned `Vec`.
Matching and parameter routes are represented by their `key`, prefixed with `\`.
Besides that `\`, all paths are URL encoded.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
# extern crate dioxus;
# use dioxus::prelude::*;
@@ -161,6 +165,7 @@ assert_eq!(sitemap, expected);
```
## Sitemaps with actual parameter values
The second variant to generate sitemaps is a bit more involved. When it
encounters a parameter route, it inserts all values with a matching `key` that
were provided to it.
@@ -169,7 +174,7 @@ Matching routes only add their path if the value matches their regex.
All paths are URL encoded.
```rust
```rust, no_run
# // Hidden lines (like this one) make the documentation tests work.
use std::collections::{BTreeMap, HashSet};
# extern crate dioxus;