mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* Bump the website-deps group across 1 directory with 15 updates Bumps the website-deps group with 14 updates in the /website directory: | Package | From | To | | --- | --- | --- | | [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.5.2` | `3.7.0` | | [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) | `3.5.2` | `3.7.0` | | [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.5.2` | `3.7.0` | | [@mdx-js/react](https://github.com/mdx-js/mdx/tree/HEAD/packages/react) | `3.0.1` | `3.1.0` | | [docusaurus-plugin-sass](https://github.com/rlamana/docusaurus-plugin-sass) | `0.2.5` | `0.2.6` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `18.3.1` | `19.0.0` | | [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `18.3.10` | `19.0.8` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `18.3.1` | `19.0.0` | | [sass](https://github.com/sass/dart-sass) | `1.79.4` | `1.83.4` | | [@docusaurus/tsconfig](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-tsconfig) | `3.5.2` | `3.7.0` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.8.0` | `8.22.0` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.8.0` | `8.22.0` | | [prettier](https://github.com/prettier/prettier) | `3.3.3` | `3.4.2` | | [typescript](https://github.com/microsoft/TypeScript) | `5.6.2` | `5.7.3` | Updates `@docusaurus/core` from 3.5.2 to 3.7.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.7.0/packages/docusaurus) Updates `@docusaurus/plugin-client-redirects` from 3.5.2 to 3.7.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.7.0/packages/docusaurus-plugin-client-redirects) Updates `@docusaurus/preset-classic` from 3.5.2 to 3.7.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.7.0/packages/docusaurus-preset-classic) Updates `@mdx-js/react` from 3.0.1 to 3.1.0 - [Release notes](https://github.com/mdx-js/mdx/releases) - [Changelog](https://github.com/mdx-js/mdx/blob/main/changelog.md) - [Commits](https://github.com/mdx-js/mdx/commits/3.1.0/packages/react) Updates `docusaurus-plugin-sass` from 0.2.5 to 0.2.6 - [Release notes](https://github.com/rlamana/docusaurus-plugin-sass/releases) - [Commits](https://github.com/rlamana/docusaurus-plugin-sass/compare/v0.2.5...v0.2.6) Updates `react` from 18.3.1 to 19.0.0 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.0.0/packages/react) Updates `@types/react` from 18.3.10 to 19.0.8 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 18.3.1 to 19.0.0 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.0.0/packages/react-dom) Updates `sass` from 1.79.4 to 1.83.4 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.79.4...1.83.4) Updates `@docusaurus/module-type-aliases` from 3.5.2 to 3.7.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.7.0/packages/docusaurus-module-type-aliases) Updates `@docusaurus/tsconfig` from 3.5.2 to 3.7.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.7.0/packages/docusaurus-tsconfig) Updates `@types/react` from 18.3.10 to 19.0.8 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@typescript-eslint/eslint-plugin` from 8.8.0 to 8.22.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.22.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.8.0 to 8.22.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.22.0/packages/parser) Updates `prettier` from 3.3.3 to 3.4.2 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.3.3...3.4.2) Updates `typescript` from 5.6.2 to 5.7.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.6.2...v5.7.3) --- updated-dependencies: - dependency-name: "@docusaurus/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@docusaurus/plugin-client-redirects" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@mdx-js/react" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: docusaurus-plugin-sass dependency-type: direct:production update-type: version-update:semver-patch dependency-group: website-deps - dependency-name: react dependency-type: direct:production update-type: version-update:semver-major dependency-group: website-deps - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-major dependency-group: website-deps - dependency-name: react-dom dependency-type: direct:production update-type: version-update:semver-major dependency-group: website-deps - dependency-name: sass dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@docusaurus/module-type-aliases" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@docusaurus/tsconfig" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-major dependency-group: website-deps - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-deps - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-deps ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix formatting * chore: bump prettier (can't reproduce element.mdx warning locally?) * workflow: show formatting diff for locally unproduceable errors * chore: format elements.mdx --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matt Yan <syan4@ualberta.ca> Co-authored-by: Siyuan Yan <44753941+Madoshakalaka@users.noreply.github.com>
504 lines
16 KiB
Plaintext
504 lines
16 KiB
Plaintext
---
|
|
title: 'Router'
|
|
description: "Yew's official router"
|
|
---
|
|
|
|
Routers in Single Page Applications (SPA) handle displaying different pages depending on what the URL is. Instead of the
|
|
default behavior of requesting a different remote resource when a link is clicked, the router instead sets the URL
|
|
locally to point to a valid route in your application. The router then detects this change and then decides what to
|
|
render.
|
|
|
|
Yew provides router support in the `yew-router` crate. To start using it, add the dependency to your `Cargo.toml`
|
|
|
|
```toml
|
|
yew-router = "0.16"
|
|
```
|
|
|
|
The utilities needed are provided under `yew_router::prelude`,
|
|
|
|
## Usage
|
|
|
|
You start by defining a `Route`.
|
|
|
|
Routes are defined as an `enum` which derives `Routable`. This enum must be `Clone + PartialEq`.
|
|
|
|
```rust
|
|
use yew_router::prelude::*;
|
|
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum Route {
|
|
#[at("/")]
|
|
Home,
|
|
#[at("/secure")]
|
|
Secure,
|
|
#[not_found]
|
|
#[at("/404")]
|
|
NotFound,
|
|
}
|
|
```
|
|
|
|
A `Route` is paired with a `<Switch />` component, which finds the variant whose path matches the browser's
|
|
current URL and passes it to the `render` callback. The callback then decides what to render. In case no path is
|
|
matched, the router navigates to the path with `not_found` attribute. If no route is specified, nothing is rendered, and
|
|
a message is logged to console stating that no route was matched.
|
|
|
|
Most of yew-router's components, in particular `<Link />` and `<Switch />`, must be (grand-)children of one of the Router components
|
|
(e.g. `<BrowserRouter />`). You usually only need a single Router in your app, most often rendered immediately by your most top-level `<App />`
|
|
component. The Router registers a context, which is needed for Links and Switches to function. An example is shown below.
|
|
|
|
:::caution
|
|
When using `yew-router` in browser environment, `<BrowserRouter />` is highly recommended.
|
|
You can find other router flavours in the [API Reference](https://docs.rs/yew-router/).
|
|
:::
|
|
|
|
```rust
|
|
use yew_router::prelude::*;
|
|
use yew::prelude::*;
|
|
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum Route {
|
|
#[at("/")]
|
|
Home,
|
|
#[at("/secure")]
|
|
Secure,
|
|
#[not_found]
|
|
#[at("/404")]
|
|
NotFound,
|
|
}
|
|
|
|
#[function_component(Secure)]
|
|
fn secure() -> Html {
|
|
let history = use_history().unwrap();
|
|
|
|
let onclick = Callback::once(move |_| history.push(Route::Home));
|
|
html! {
|
|
<div>
|
|
<h1>{ "Secure" }</h1>
|
|
<button {onclick}>{ "Go Home" }</button>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
fn switch(routes: &Route) -> Html {
|
|
match routes {
|
|
Route::Home => html! { <h1>{ "Home" }</h1> },
|
|
Route::Secure => html! {
|
|
<Secure />
|
|
},
|
|
Route::NotFound => html! { <h1>{ "404" }</h1> },
|
|
}
|
|
}
|
|
|
|
#[function_component(Main)]
|
|
fn app() -> Html {
|
|
html! {
|
|
<BrowserRouter>
|
|
<Switch<Route> render={Switch::render(switch)} /> // <- must be child of <BrowserRouter>
|
|
</BrowserRouter>
|
|
}
|
|
}
|
|
```
|
|
|
|
### Path Segments
|
|
|
|
It is also possible to extract information from a route.
|
|
You can then access the post's id inside `<Switch />` and forward it to the appropriate component via properties.
|
|
|
|
```rust
|
|
use yew::prelude::*;
|
|
use yew_router::prelude::*;
|
|
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum Route {
|
|
#[at("/")]
|
|
Home,
|
|
#[at("/post/:id")]
|
|
Post { id: String },
|
|
}
|
|
|
|
fn switch(route: &Route) -> Html {
|
|
match route {
|
|
Route::Home => html! { <h1>{ "Home" }</h1> },
|
|
Route::Post { id } => html! {<p>{format!("You are looking at Post {}", id)}</p>},
|
|
}
|
|
}
|
|
```
|
|
|
|
:::note
|
|
You can have a normal `Post` variant instead of `Post {id: String}` too. For example when `Post` is rendered
|
|
with another router, the field can then be redundant as the other router is able to match and handle the path. See the
|
|
[Nested Router](#nested-router) section below for details
|
|
:::
|
|
|
|
Note the fields must implement `Clone + PartialEq` as part of the `Route` enum. They must also implement
|
|
`std::fmt::Display` and `std::str::FromStr` for serialization and deserialization. Primitive types like integer, float,
|
|
and String already satisfy the requirements.
|
|
|
|
In case when the form of the path matches, but the deserialization fails (as per `FromStr`). The router will consider
|
|
the route as unmatched and try to render the not found route (or a blank page if the not found route is unspecified).
|
|
|
|
Consider this example:
|
|
|
|
```rust ,ignore
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum Route {
|
|
#[at("/news/:id")]
|
|
News { id: u8 },
|
|
#[not_found]
|
|
#[at("/404")]
|
|
NotFound,
|
|
}
|
|
// switch function renders News and id as is. Omitted here.
|
|
```
|
|
|
|
When the segment goes over 255, `u8::from_str()` fails with `ParseIntError`, the router will then consider the route
|
|
unmatched.
|
|
|
|

|
|
|
|
For more information about the route syntax and how to bind parameters, check
|
|
out [route-recognizer](https://docs.rs/route-recognizer/0.3.1/route_recognizer/#routing-params).
|
|
|
|
### History and Location
|
|
|
|
The router provides a universal `History` and `Location` struct which can be used to access routing information. They
|
|
can be retrieved by hooks or convenient functions on `ctx.link()`.
|
|
|
|
They have a couple flavours:
|
|
|
|
#### `AnyHistory` and `AnyLocation`
|
|
|
|
These types are available with all routers and should be used whenever possible. They implement a subset
|
|
of `window.history` and `window.location`.
|
|
|
|
You can access them using the following hooks:
|
|
|
|
- `use_history`
|
|
- `use_location`
|
|
|
|
#### `BrowserHistory` and `BrowserLocation`
|
|
|
|
These are only available when `<BrowserRouter />` is used. They provide additional functionality that is not available
|
|
in `AnyHistory` and
|
|
`AnyLocation` (such as: `location.host`).
|
|
|
|
### Navigation
|
|
|
|
`yew_router` provides a handful of tools to work with navigation.
|
|
|
|
#### Link
|
|
|
|
A `<Link/>` renders as an `<a>` element, the `onclick` event handler will call
|
|
[preventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault), and push the targeted page to the
|
|
history and render the desired page, which is what should be expected from a Single Page App. The default onclick of a
|
|
normal anchor element would reload the page.
|
|
|
|
The `<Link/>` element also passes its children to the `<a>` element. Consider it a replacement of `<a/>` for in-app
|
|
routes. Except you supply a `to` attribute instead of a `href`. An example usage:
|
|
|
|
```rust ,ignore
|
|
<Link<Route> to={Route::Home}>{ "click here to go home" }</Link<Route>>
|
|
```
|
|
|
|
Struct variants work as expected too:
|
|
|
|
```rust ,ignore
|
|
<Link<Route> to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew v0.19 out now!" }</Link<Route>>
|
|
```
|
|
|
|
#### History API
|
|
|
|
History API is provided for both function components and struct components. They can enable callbacks to change the
|
|
route. An `AnyHistory` instance can be obtained in either cases to manipulate the route.
|
|
|
|
##### Function Components
|
|
|
|
For function components, the `use_history` hook re-renders the component and returns the current route whenever the
|
|
history changes. Here's how to implement a button that navigates to the `Home` route when clicked.
|
|
|
|
```rust ,ignore
|
|
#[function_component(MyComponent)]
|
|
pub fn my_component() -> Html {
|
|
let history = use_history().unwrap();
|
|
let onclick = Callback::once(move |_| history.push(Route::Home));
|
|
|
|
html! {
|
|
<>
|
|
<button {onclick}>{"Click to go home"}</button>
|
|
</>
|
|
}
|
|
}
|
|
```
|
|
|
|
:::tip
|
|
The example here uses `Callback::once`. Use a normal callback if the target route can be the same with the route
|
|
the component is in. For example when you have a logo button on every page the that goes back to home when clicked,
|
|
clicking that button twice on home page causes the code to panic because the second click pushes an identical Home route
|
|
and won't trigger a re-render of the element.
|
|
|
|
In other words, only use `Callback::once` when you are sure the target route is different. Or use normal callbacks only
|
|
to be safe.
|
|
:::
|
|
|
|
If you want to replace the current history instead of pushing a new history onto the stack, use `history.replace()`
|
|
instead of `history.push()`.
|
|
|
|
You may notice `history` has to move into the callback, so it can't be used again for other callbacks. Luckily `history`
|
|
implements `Clone`, here's for example how to have multiple buttons to different routes:
|
|
|
|
```rust ,ignore
|
|
use yew::prelude::*;
|
|
use yew_router::prelude::*;
|
|
|
|
#[function_component(NavItems)]
|
|
pub fn nav_items() -> Html {
|
|
let history = use_history().unwrap();
|
|
|
|
let go_home_button = {
|
|
let history = history.clone();
|
|
let onclick = Callback::once(move |_| history.push(Route::Home));
|
|
html! {
|
|
<button {onclick}>{"click to go home"}</button>
|
|
}
|
|
};
|
|
|
|
let go_to_first_post_button = {
|
|
let history = history.clone();
|
|
let onclick = Callback::once(move |_| history.push(Route::Post { id: "first-post".to_string() }));
|
|
html! {
|
|
<button {onclick}>{"click to go the first post"}</button>
|
|
}
|
|
};
|
|
|
|
let go_to_secure_button = {
|
|
let onclick = Callback::once(move |_| history.push(Route::Secure));
|
|
html! {
|
|
<button {onclick}>{"click to go to secure"}</button>
|
|
}
|
|
};
|
|
|
|
html! {
|
|
<>
|
|
{go_home_button}
|
|
{go_to_first_post_button}
|
|
{go_to_secure_button}
|
|
</>
|
|
}
|
|
}
|
|
```
|
|
|
|
##### Struct Components
|
|
|
|
For struct components, the `AnyHistory` instance can be obtained through the `ctx.link().history()` API. The rest is
|
|
identical with the function component case. Here's an example of a view function that renders a single button.
|
|
|
|
```rust ,ignore
|
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
let history = ctx.link().history().unwrap();
|
|
let onclick = Callback::once(move |_| history.push(MainRoute::Home));
|
|
html!{
|
|
<button {onclick}>{"Go Home"}</button>
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Redirect
|
|
|
|
`yew-router` also provides a `<Redirect/>` element in the prelude. It can be used to achieve similar effects as the
|
|
history API. The element accepts a
|
|
`to` attribute as the target route. When a `<Redirect/>` element is rendered, it internally calls `history.push()` and
|
|
changes the route. Here is an example:
|
|
|
|
```rust ,ignore
|
|
#[function_component(SomePage)]
|
|
fn some_page() -> Html {
|
|
// made-up hook `use_user`
|
|
let user = match use_user() {
|
|
Some(user) => user,
|
|
// an early return that redirects to the login page
|
|
// technicality: `Redirect` actually renders an empty html. But since it also pushes history, the target page
|
|
// shows up immediately. Consider it a "side-effect" component.
|
|
None => return html! {
|
|
<Redirect<Route> to={Route::Login}/>
|
|
},
|
|
};
|
|
// ... actual page content.
|
|
}
|
|
```
|
|
|
|
:::tip `Redirect` vs `history`, which to use
|
|
The history API is the only way to manipulate route in callbacks.
|
|
While `<Redirect/>` can be used as return values in a component. You might also want to use `<Redirect/>` in other
|
|
non-component context, for example in the switch function of a [Nested Router](#nested-router).
|
|
:::
|
|
|
|
### Listening to Changes
|
|
|
|
#### Function Components
|
|
|
|
Alongside the `use_history` hook, there are also `use_location` and `use_route`. Your components will re-render when
|
|
provided values change.
|
|
|
|
#### Struct Components
|
|
|
|
In order to react on route changes, you can pass a callback closure to the `add_history_listener()` method of `ctx.link()`.
|
|
|
|
:::note
|
|
The history listener will get unregistered once it is dropped. Make sure to store the handle inside your
|
|
component state.
|
|
:::
|
|
|
|
```rust ,ignore
|
|
fn create(ctx: &Context<Self>) -> Self {
|
|
let listener = ctx.link()
|
|
.add_history_listener(ctx.link().callback(
|
|
// handle event
|
|
))
|
|
.unwrap();
|
|
MyComponent {
|
|
_listener: listener
|
|
}
|
|
}
|
|
```
|
|
|
|
`ctx.link().location()` and `ctx.link().route::<R>()` can also be used to retrieve the location and the route once.
|
|
|
|
### Query Parameters
|
|
|
|
#### Specifying query parameters when navigating
|
|
|
|
In order to specify query parameters when navigating to a new route, use either `history.push_with_query` or
|
|
the `history.replace_with_query` functions. It uses `serde` to serialize the parameters into query string for the URL so
|
|
any type that implements `Serialize` can be passed. In its simplest form this is just a `HashMap` containing string
|
|
pairs.
|
|
|
|
#### Obtaining query parameters for current route
|
|
|
|
`location.query` is used to obtain the query parameters. It uses `serde` to deserialize the parameters from query string
|
|
in the URL.
|
|
|
|
## Nested Router
|
|
|
|
Nested router can be useful when the app grows larger. Consider the following router structure:
|
|
|
|
<!--
|
|
The graph is produced with the following code, with graphviz.
|
|
To reproduce. Save the code in a file, say `input.dot`,
|
|
And run `$ dot -Tsvg input.dot -o nested-router.svg`
|
|
|
|
digraph {
|
|
bgcolor=transparent
|
|
node [shape=box style="filled, rounded" fillcolor=white]
|
|
Home; News; Contact; "Not Found"; Profile; Friends; Theme; SettingsNotFound [label="Not Found"];
|
|
|
|
node [fillcolor=lightblue style="filled, rounded"]
|
|
"Main Router"; "Settings Router";
|
|
|
|
"Main Router" -> {Home News Contact "Not Found" "Settings Router"} [arrowhead=none]
|
|
"Settings Router" -> {SettingsNotFound Profile Friends Theme } [arrowhead=none]
|
|
SettingsNotFound -> "Not Found" [constraint=false]
|
|
}
|
|
-->
|
|
|
|
<!--
|
|
Also the dark themed version:
|
|
digraph {
|
|
bgcolor=transparent
|
|
node [shape=box style="filled, rounded" fillcolor=grey color=white fontcolor=white]
|
|
Home; News; Contact; "Not Found"; Profile; Friends; Theme; SettingsNotFound [label="Not Found"];
|
|
|
|
node [fillcolor=lightblue style="filled, rounded" color=white fontcolor=black]
|
|
"Main Router"; "Settings Router";
|
|
|
|
"Main Router" -> {Home News Contact "Not Found" "Settings Router"} [arrowhead=none color=white]
|
|
"Settings Router" -> {SettingsNotFound Profile Friends Theme } [arrowhead=none color=white]
|
|
SettingsNotFound -> "Not Found" [constraint=false color=white]
|
|
}
|
|
-->
|
|
|
|
import useBaseUrl from '@docusaurus/useBaseUrl'
|
|
import ThemedImage from '@theme/ThemedImage'
|
|
|
|
<ThemedImage
|
|
alt="nested router structure"
|
|
sources={{
|
|
light: useBaseUrl('/img/nested-router-light.svg'),
|
|
dark: useBaseUrl('/img/nested-router-dark.svg'),
|
|
}}
|
|
/>
|
|
;
|
|
|
|
The nested `SettingsRouter` handles all urls that start with `/settings`. Additionally, it redirects urls that are not
|
|
matched to the main `NotFound` route. So `/settings/gibberish` will redirect to `/404`.
|
|
|
|
It can be implemented with the following code:
|
|
|
|
```rust
|
|
use yew::prelude::*;
|
|
use yew_router::prelude::*;
|
|
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum MainRoute {
|
|
#[at("/")]
|
|
Home,
|
|
#[at("/news")]
|
|
News,
|
|
#[at("/contact")]
|
|
Contact,
|
|
#[at("/settings/:s")]
|
|
Settings,
|
|
#[not_found]
|
|
#[at("/404")]
|
|
NotFound,
|
|
}
|
|
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum SettingsRoute {
|
|
#[at("/settings/profile")]
|
|
Profile,
|
|
#[at("/settings/friends")]
|
|
Friends,
|
|
#[at("/settings/theme")]
|
|
Theme,
|
|
#[not_found]
|
|
#[at("/settings/404")]
|
|
NotFound,
|
|
}
|
|
|
|
fn switch_main(route: &MainRoute) -> Html {
|
|
match route {
|
|
MainRoute::Home => html! {<h1>{"Home"}</h1>},
|
|
MainRoute::News => html! {<h1>{"News"}</h1>},
|
|
MainRoute::Contact => html! {<h1>{"Contact"}</h1>},
|
|
MainRoute::Settings => html! {
|
|
<Switch<SettingsRoute> render={Switch::render(switch_settings)} />
|
|
},
|
|
MainRoute::NotFound => html! {<h1>{"Not Found"}</h1>},
|
|
}
|
|
}
|
|
|
|
fn switch_settings(route: &SettingsRoute) -> Html {
|
|
match route {
|
|
SettingsRoute::Profile => html! {<h1>{"Profile"}</h1>},
|
|
SettingsRoute::Friends => html! {<h1>{"Friends"}</h1>},
|
|
SettingsRoute::Theme => html! {<h1>{"Theme"}</h1>},
|
|
SettingsRoute::NotFound => html! {
|
|
<Redirect<MainRoute> to={MainRoute::NotFound}/>
|
|
}
|
|
}
|
|
}
|
|
|
|
#[function_component(App)]
|
|
pub fn app() -> Html {
|
|
html! {
|
|
<BrowserRouter>
|
|
<Switch<MainRoute> render={Switch::render(switch_main)} />
|
|
</BrowserRouter>
|
|
}
|
|
}
|
|
```
|
|
|
|
## Relevant examples
|
|
|
|
- [Router](https://github.com/yewstack/yew/tree/yew-v0.19.3/examples/router)
|