1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040 |
- <!DOCTYPE HTML>
- <html lang="en" class="sidebar-visible no-js light">
- <head>
- <!-- Book generated using mdBook -->
- <meta charset="UTF-8">
- <title>Dioxus Router</title>
- <meta name="robots" content="noindex" />
- <!-- Custom HTML head -->
-
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
- <meta name="description" content="">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="theme-color" content="#ffffff" />
- <link rel="icon" href="favicon.svg">
- <link rel="shortcut icon" href="favicon.png">
- <link rel="stylesheet" href="css/variables.css">
- <link rel="stylesheet" href="css/general.css">
- <link rel="stylesheet" href="css/chrome.css">
- <link rel="stylesheet" href="css/print.css" media="print">
- <!-- Fonts -->
- <link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
- <link rel="stylesheet" href="fonts/fonts.css">
- <!-- Highlight.js Stylesheets -->
- <link rel="stylesheet" href="highlight.css">
- <link rel="stylesheet" href="tomorrow-night.css">
- <link rel="stylesheet" href="ayu-highlight.css">
- <!-- Custom theme stylesheets -->
- </head>
- <body>
- <!-- Provide site root to javascript -->
- <script type="text/javascript">
- var path_to_root = "";
- var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
- </script>
- <!-- Work around some values being stored in localStorage wrapped in quotes -->
- <script type="text/javascript">
- try {
- var theme = localStorage.getItem('mdbook-theme');
- var sidebar = localStorage.getItem('mdbook-sidebar');
- if (theme.startsWith('"') && theme.endsWith('"')) {
- localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
- }
- if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
- localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
- }
- } catch (e) { }
- </script>
- <!-- Set the theme before any content is loaded, prevents flash -->
- <script type="text/javascript">
- var theme;
- try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
- if (theme === null || theme === undefined) { theme = default_theme; }
- var html = document.querySelector('html');
- html.classList.remove('no-js')
- html.classList.remove('light')
- html.classList.add(theme);
- html.classList.add('js');
- </script>
- <!-- Hide / unhide sidebar before it is displayed -->
- <script type="text/javascript">
- var html = document.querySelector('html');
- var sidebar = 'hidden';
- if (document.body.clientWidth >= 1080) {
- try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
- sidebar = sidebar || 'visible';
- }
- html.classList.remove('sidebar-visible');
- html.classList.add("sidebar-" + sidebar);
- </script>
- <nav id="sidebar" class="sidebar" aria-label="Table of contents">
- <div class="sidebar-scrollbox">
- <ol class="chapter"><li class="chapter-item expanded affix "><a href="index.html">Introduction</a></li><li class="chapter-item expanded affix "><li class="part-title">Features</li><li class="chapter-item expanded "><a href="features/index.html"><strong aria-hidden="true">1.</strong> Adding the Router to Your Application</a></li><li class="chapter-item expanded "><a href="features/routes/index.html"><strong aria-hidden="true">2.</strong> Defining Routes</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="features/routes/nested.html"><strong aria-hidden="true">2.1.</strong> Nested Routes</a></li><li class="chapter-item expanded "><a href="features/routes/catch_all.html"><strong aria-hidden="true">2.2.</strong> Catch All Routes</a></li><li class="chapter-item expanded "><a href="features/routes/matching.html"><strong aria-hidden="true">2.3.</strong> Matching Routes</a></li><li class="chapter-item expanded "><a href="features/routes/fallback.html"><strong aria-hidden="true">2.4.</strong> Fallback Routes (404 page)</a></li><li class="chapter-item expanded "><a href="features/routes/multiple-and-redirect.html"><strong aria-hidden="true">2.5.</strong> Multiple Components & Redirects</a></li></ol></li><li class="chapter-item expanded "><a href="features/outlets.html"><strong aria-hidden="true">3.</strong> Outlets</a></li><li class="chapter-item expanded "><a href="features/navigation/index.html"><strong aria-hidden="true">4.</strong> Links & Navigation</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="features/navigation/name.html"><strong aria-hidden="true">4.1.</strong> Named Navigation</a></li><li class="chapter-item expanded "><a href="features/navigation/external.html"><strong aria-hidden="true">4.2.</strong> External Navigation</a></li><li class="chapter-item expanded "><a href="features/navigation/programmatic.html"><strong aria-hidden="true">4.3.</strong> Programmatic Navigation</a></li></ol></li><li class="chapter-item expanded "><a href="features/query.html"><strong aria-hidden="true">5.</strong> Query</a></li><li class="chapter-item expanded "><a href="features/failures/index.html"><strong aria-hidden="true">6.</strong> Navigation Failures</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="features/failures/named.html"><strong aria-hidden="true">6.1.</strong> Named Navigation Failure</a></li><li class="chapter-item expanded "><a href="features/failures/external.html"><strong aria-hidden="true">6.2.</strong> External Navigation Failure</a></li><li class="chapter-item expanded "><a href="features/failures/redirection-limit.html"><strong aria-hidden="true">6.3.</strong> Redirection Limit Failure</a></li></ol></li><li class="chapter-item expanded "><a href="features/history-providers.html"><strong aria-hidden="true">7.</strong> History Providers</a></li><li class="chapter-item expanded "><a href="features/history-buttons.html"><strong aria-hidden="true">8.</strong> History Buttons</a></li><li class="chapter-item expanded "><a href="features/sitemap-generation.html"><strong aria-hidden="true">9.</strong> Sitemap Generation</a></li><li class="chapter-item expanded "><a href="features/routing-update-callback.html"><strong aria-hidden="true">10.</strong> Routing Update Callback</a></li><li class="chapter-item expanded affix "><li class="part-title">Example Project</li><li class="chapter-item expanded "><a href="example/index.html"><strong aria-hidden="true">11.</strong> Overview</a></li><li class="chapter-item expanded "><a href="example/first-route.html"><strong aria-hidden="true">12.</strong> Creating Our First Route</a></li><li class="chapter-item expanded "><a href="example/building-a-nest.html"><strong aria-hidden="true">13.</strong> Building a Nest</a></li><li class="chapter-item expanded "><a href="example/navigation-targets.html"><strong aria-hidden="true">14.</strong> Navigation Targets</a></li><li class="chapter-item expanded "><a href="example/redirection-perfection.html"><strong aria-hidden="true">15.</strong> Redirection Perfection</a></li><li class="chapter-item expanded "><a href="example/full-code.html"><strong aria-hidden="true">16.</strong> Full Code</a></li></ol>
- </div>
- <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
- </nav>
- <div id="page-wrapper" class="page-wrapper">
- <div class="page">
- <div id="menu-bar-hover-placeholder"></div>
- <div id="menu-bar" class="menu-bar sticky bordered">
- <div class="left-buttons">
- <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
- <i class="fa fa-bars"></i>
- </button>
- <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
- <i class="fa fa-paint-brush"></i>
- </button>
- <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
- <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
- <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
- <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
- <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
- <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
- </ul>
- <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
- <i class="fa fa-search"></i>
- </button>
- </div>
- <h1 class="menu-title">Dioxus Router</h1>
- <div class="right-buttons">
- <a href="print.html" title="Print this book" aria-label="Print this book">
- <i id="print-button" class="fa fa-print"></i>
- </a>
- </div>
- </div>
- <div id="search-wrapper" class="hidden">
- <form id="searchbar-outer" class="searchbar-outer">
- <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
- </form>
- <div id="searchresults-outer" class="searchresults-outer hidden">
- <div id="searchresults-header" class="searchresults-header"></div>
- <ul id="searchresults">
- </ul>
- </div>
- </div>
- <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
- <script type="text/javascript">
- document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
- document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
- Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
- link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
- });
- </script>
- <div id="content" class="content">
- <main>
- <h1 id="introduction"><a class="header" href="#introduction">Introduction</a></h1>
- <p>Whether or not you are building a website, desktop app, or mobile app,
- splitting your app's views into "pages" can be an effective method for
- organization and maintainability.</p>
- <p>For this purpose, Dioxus provides a router. To start utilizing it, add it as a
- dependency in your <code>Cargo.toml</code> file:</p>
- <pre><code class="language-toml">[dependencies]
- # use this for native apps
- dioxus-router = "*"
- #use this for web apps
- dioxus-router = { version = "*", features = ["web"] }
- # in both cases replace * with the current version
- </code></pre>
- <p>You can also use the <code>cargo</code> command to add the dependency:</p>
- <pre><code class="language-sh">$ cargo add dioxus-router
- $ cargo add dioxus-router --features web
- </code></pre>
- <blockquote>
- <p>If you are not familiar with Dioxus itself, check out the <a href="https://dioxuslabs.com/guide/">Dioxus book</a>
- first.</p>
- </blockquote>
- <p>This book is intended to get you up to speed with Dioxus Router. It is split
- into two sections:</p>
- <ol>
- <li>The <a href="./features/index.html">Features</a> part explains individual features in
- depth. You can read it start to finish, or you can read individual chapters
- in whatever order you want.</li>
- <li>If you prefer a learning-by-doing approach, you can check ouf the
- <em><a href="./example/introduction.html">example project</a></em>. It guides you through
- creating a dioxus app, setting up the router and using some of its
- functionality.</li>
- </ol>
- <blockquote>
- <p>Please note that this is not the only documentation for the Dioxus Router. You
- can also check out the <a href="https://docs.rs/dioxus-router/">API Docs</a>.</p>
- </blockquote>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="adding-the-router-to-your-application"><a class="header" href="#adding-the-router-to-your-application">Adding the Router to Your Application</a></h1>
- <p>In this chapter we will learn how to add the router to our app. By it self, this
- is not very useful. However, it is a prerequisite for all the functionality
- described in the other chapters.</p>
- <blockquote>
- <p>Make sure you added the <code>dioxus-router</code> dependency as explained in the
- <a href="features/../index.html">introduction</a>.</p>
- </blockquote>
- <p>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 <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a> hook</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- <span class="boring">extern crate dioxus_ssr;
- </span>
- // This is the component we pass to dioxus when launching our app.
- fn App(cx: Scope) -> Element {
- // Here we add the router. All components inside `App` have access to its
- // functionality.
- let routes = use_router(
- cx,
- // The router can be configured with this parameter.
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span> ..Default::default()
- },
- // This tells the router about all the routes in our application. As we
- // don't have any, we pass an empty segment
- &|| Segment::empty()
- );
- render! {
- h1 { "Our sites title" }
- // The Outlet tells the Router where to render active content.
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">let _ = vdom.rebuild();
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> dioxus_ssr::render(&vdom),
- </span><span class="boring"> "<h1>Our sites title</h1>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="defining-routes"><a class="header" href="#defining-routes">Defining Routes</a></h1>
- <p>When creating a router we need to pass it a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>. It tells the router
- about all the routes of our app.</p>
- <h2 id="example-content"><a class="header" href="#example-content">Example content</a></h2>
- <p>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.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- fn Index(cx: Scope) -> Element {
- render! {
- h1 { "Welcome to our test site!" }
- }
- }
- fn Other(cx: Scope) -> Element {
- render! {
- p { "some other content" }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="index-routes"><a class="header" href="#index-routes">Index routes</a></h2>
- <p>The easiest thing to do is to define an index route.</p>
- <p>Index routes act very similar to <code>index.html</code> files in most web servers. They
- are active, when we don't specify a route.</p>
- <blockquote>
- <p>Note that we wrap our <code>Index</code> component with <a href="https://docs.rs/dioxus-router/latest/dioxus_router/prelude/fn.comp.html"><code>comp</code></a>. This is because of
- rust type system requirements.</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Index(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- ..Default::default()
- },
- &|| Segment::content(comp(Index))
- );
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="fixed-routes"><a class="header" href="#fixed-routes">Fixed routes</a></h2>
- <p>It is almost as easy to define a fixed route.</p>
- <p>Fixed routes work similar to how web servers treat files. They are active, when
- specified in the path. In the example, the path must be <code>/other</code>.</p>
- <blockquote>
- <p>The path will be URL decoded before checking if it matches our route.</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Index(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Other(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- ..Default::default()
- },
- &|| Segment::content(comp(Index)).fixed("other", comp(Other))
- // ^ note the absence of a / prefix
- );
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="full-code"><a class="header" href="#full-code">Full Code</a></h2>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::{history::MemoryHistory, prelude::*};
- <span class="boring">extern crate dioxus_ssr;
- </span>
- fn Index(cx: Scope) -> Element {
- render! {
- h1 { "Welcome to our test site!" }
- }
- }
- fn Other(cx: Scope) -> Element {
- render! {
- p { "some other content" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/other").unwrap()),
- </span> ..Default::default()
- },
- &|| Segment::content(comp(Index)).fixed("other", comp(Other))
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> dioxus_ssr::render(&vdom),
- </span><span class="boring"> "<p>some other content</p>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="nested-routes"><a class="header" href="#nested-routes">Nested Routes</a></h1>
- <p>When developing bigger applications we often want to nest routes within each
- other. As an example, we might want to organize a settings menu using this
- pattern:</p>
- <pre><code class="language-plain">└ Settings
- ├ General Settings (displayed when opening the settings)
- ├ Change Password
- └ Privacy Settings
- </code></pre>
- <p>We might want to map this structure to these paths and components:</p>
- <pre><code class="language-plain">/settings -> Settings { GeneralSettings }
- /settings/password -> Settings { PWSettings }
- /settings/privacy -> Settings { PrivacySettings }
- </code></pre>
- <p>Nested routes allow us to do this.</p>
- <h2 id="route-depth"><a class="header" href="#route-depth">Route Depth</a></h2>
- <p>With nesting routes, the router manages content on multiple levels. In our
- example, when the path is <code>/settings</code>, there are two levels of content:</p>
- <ol start="0">
- <li>The <code>Settings</code> component</li>
- <li>The <code>GeneralSettings</code> component</li>
- </ol>
- <p>Dioxus Router uses the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> component to actually render content, but each
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> can only render content from one level. This means that for the
- content of nested routes to actually be rendered, we also need nested
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>s.</p>
- <h2 id="defining-the-content-components"><a class="header" href="#defining-the-content-components">Defining the content components</a></h2>
- <p>We start by creating the components we want the router to render.</p>
- <p>Take a look at the <code>Settings</code> component. When it gets rendered by an <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>,
- it will render a second <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>. Thus the second <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> is nested within
- the first one, and will in turn render our nested content.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>fn Settings(cx: Scope) -> Element {
- render! {
- h1 { "Settings" }
- Outlet { }
- }
- }
- fn GeneralSettings(cx: Scope) -> Element {
- render! {
- h2 { "General Settings" }
- }
- }
- fn PWSettings(cx: Scope) -> Element {
- render! {
- h2 { "Password Settings" }
- }
- }
- fn PrivacySettings(cx: Scope) -> Element {
- render! {
- h2 { "Privacy Settings" }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="defining-the-root-segment"><a class="header" href="#defining-the-root-segment">Defining the root <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a></a></h2>
- <p>Now we create the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> that we will pass to the router.</p>
- <p>Note that we wrap <code>comp(Settings)</code> within a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Route.html"><code>Route</code></a>. For this exact code that
- is unnecessary, as this would be done automatically. However, in the next step
- we'll use a method of <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Route.html"><code>Route</code></a>, so we might as well add this now.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Settings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- ..Default::default()
- },
- &|| Segment::empty().fixed("settings", Route::content(comp(Settings)))
- );
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="defining-the-nested-segment"><a class="header" href="#defining-the-nested-segment">Defining the nested <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a></a></h2>
- <p>In order to create nested routes we need to create a nested <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>. We then
- pass it to the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Route.html"><code>Route</code></a> on the root segment.</p>
- <blockquote>
- <p>A <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> always refers to one exact segment of the path.</p>
- <p>https://router.example/<code>root_segment</code>/<code>first_nested_segment</code>/<code>second_nested_segment</code>/...</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Settings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn GeneralSettings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PWSettings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PrivacySettings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- ..Default::default()
- },
- &|| Segment::empty().fixed(
- "settings",
- Route::content(comp(Settings)).nested(
- Segment::content(comp(GeneralSettings))
- .fixed("password", comp(PWSettings))
- .fixed("privacy", comp(PrivacySettings))
- )
- )
- );
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="full-code-1"><a class="header" href="#full-code-1">Full Code</a></h2>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::{history::MemoryHistory, prelude::*};
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">
- </span>fn Settings(cx: Scope) -> Element {
- render! {
- h1 { "Settings" }
- Outlet { }
- }
- }
- fn GeneralSettings(cx: Scope) -> Element {
- render! {
- h2 { "General Settings" }
- }
- }
- fn PWSettings(cx: Scope) -> Element {
- render! {
- h2 { "Password Settings" }
- }
- }
- fn PrivacySettings(cx: Scope) -> Element {
- render! {
- h2 { "Privacy Settings" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/settings/privacy").unwrap()),
- </span> ..Default::default()
- },
- &|| Segment::empty().fixed(
- "settings",
- Route::content(comp(Settings)).nested(
- Segment::content(comp(GeneralSettings))
- .fixed("password", comp(PWSettings))
- .fixed("privacy", comp(PrivacySettings))
- )
- )
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> dioxus_ssr::render(&vdom),
- </span><span class="boring"> "<h1>Settings</h1><h2>Privacy Settings</h2>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="catch-all-routes"><a class="header" href="#catch-all-routes">Catch All Routes</a></h1>
- <p>Many modern web apps store parameters within their current path. This allows
- users to share URLs that link to a specific bit of content. We can create this
- functionality with catch all routes.</p>
- <blockquote>
- <p>If you want to change what route is active based on the format of the
- parameter, see <a href="features/routes/./matching.html">Matching Routes</a>.</p>
- </blockquote>
- <blockquote>
- <p>The parameter will be URL decoded.</p>
- </blockquote>
- <h2 id="creating-a-content-component"><a class="header" href="#creating-a-content-component">Creating a content component</a></h2>
- <p>We start by creating a component that uses the parameters value.</p>
- <p>We can get the current state of the router using the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_route.html"><code>use_route</code></a> hook. From
- that state we can extract the current value of our parameter by using a key we
- will later also define on our route.</p>
- <blockquote>
- <p>It is <strong>VERY IMPORTANT</strong> to drop the object returned by the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_route.html"><code>use_route</code></a>
- hook once our component finished rendering. Otherwise the entire router will
- be frozen.</p>
- </blockquote>
- <blockquote>
- <p>The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_route.html"><code>use_route</code></a> hook can only be used in components nested within a
- component that called <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a>.</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- struct Name;
- fn Greeting(cx: Scope) -> Element {
- let route = use_route(cx).expect("is nested within a Router component");
- let name = route.parameter::<Name>()
- .map(|name| name.clone())
- .unwrap_or(String::from("world"));
- render! {
- p { "Hello, {name}!" }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="defining-the-routes"><a class="header" href="#defining-the-routes">Defining the routes</a></h2>
- <p>Now we can define our route. Unlike a fixed <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Route.html"><code>Route</code></a>, a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.ParameterRoute.html"><code>ParameterRoute</code></a>
- needs two arguments to be created.</p>
- <blockquote>
- <p>Also note that each <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> can have exactly one parameter or
- <a href="features/routes/./fallback.html">fallback route</a>.</p>
- <p>For that reason, the example below would not work in practice, but showing
- both forms (explicit and short) is more important for this example.</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Greeting(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>struct Name;
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- ..Default::default()
- },
- &|| {
- Segment::empty()
- .catch_all(ParameterRoute::content::<Name>(comp(Greeting)))
- .catch_all((comp(Greeting), Name { })) // same in short
- }
- );
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="interaction-with-other-routes"><a class="header" href="#interaction-with-other-routes">Interaction with other routes</a></h2>
- <p>Each individual <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> can only ever have one active route. This means that
- when a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> has more than just a catch all route, the router has to
- decide which is active. It does that this way:</p>
- <ol start="0">
- <li>If the segment is not specified (i.e. <code>/</code>), then the index route will be
- active.</li>
- <li>If a <a href="features/routes/./index.html#fixed-routes"><em>fixed</em></a> route matches the current path, it
- will be active.</li>
- <li>If a <a href="features/routes/./matching.html"><em>matching</em> route</a> matches the current path, it will be
- active. <em>Matching</em> routes are checked in the order they are defined.</li>
- <li>If neither a <em>fixed</em> nor a <em>matching</em> route is active, the <em>catch all</em> route
- or <a href="features/routes/./fallback.html"><em>fallback</em> route</a> will be.</li>
- </ol>
- <p>Step 0 means that if we want a parameter to be empty, that needs to be specified
- by the path, i.e. <code>//</code>.</p>
- <blockquote>
- <p>Be careful with using catch all routes on the root <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>. Navigating to
- paths starting with <code>//</code> will <strong>NOT</strong> work. This is not a limitation of the
- router, but rather of how relative URLs work.</p>
- <p>If you absolutely need an empty parameter on the root <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>, a URL like
- this <em>could</em> work:</p>
- <ul>
- <li><code>https://your-site.example//</code> for web sites</li>
- <li><code>dioxus://index.html//</code> for desktop apps</li>
- </ul>
- </blockquote>
- <h2 id="full-code-2"><a class="header" href="#full-code-2">Full Code</a></h2>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::{history::MemoryHistory, prelude::*};
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">
- </span>struct Name;
- fn Greeting(cx: Scope) -> Element {
- let route = use_route(cx).expect("is nested within a Router component");
- let name = route.parameter::<Name>()
- .map(|name| name.clone())
- .unwrap_or(String::from("world"));
- render! {
- p { "Hello, {name}!" }
- }
- }
- fn App(cx: Scope) -> Element {
- let routes = use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/Dioxus").unwrap()),
- </span> ..Default::default()
- },
- &|| Segment::empty().catch_all((comp(Greeting), Name { }))
- );
- // ...
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> dioxus_ssr::render(&vdom),
- </span><span class="boring"> "<p>Hello, Dioxus!</p>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="matching-routes"><a class="header" href="#matching-routes">Matching Routes</a></h1>
- <blockquote>
- <p>Make sure you understand how <a href="features/routes/./catch_all.html">catch all routes</a> work before
- reading this page.</p>
- </blockquote>
- <p>When accepting parameters via the path, some complex applications might need to
- decide what route should be active based on the format of that parameter.
- <em>Matching</em> routes make it easy to implement such behavior.</p>
- <blockquote>
- <p>The parameter will be URL decoded, both for checking if the route is active
- and when it is provided to the application.</p>
- </blockquote>
- <blockquote>
- <p>The example below is only for showing <em>matching route</em> functionality. It is
- unfit for all other purposes.</p>
- </blockquote>
- <h2 id="code-example"><a class="header" href="#code-example">Code Example</a></h2>
- <blockquote>
- <p>Notice that the parameter of a <em>matching route</em> has the same type as a
- <a href="features/routes/./catch_all.html"><em>catch all route</em></a>.</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::{history::MemoryHistory, prelude::*};
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">extern crate regex;
- </span>use regex::Regex;
- struct Name;
- fn GreetingFemale(cx: Scope) -> Element {
- let route = use_route(cx).unwrap();
- let name = route.parameter::<Name>()
- .map(|name| {
- let mut name = name.to_string();
- name.remove(0);
- name
- })
- .unwrap_or(String::from("Anonymous"));
- render! {
- p { "Hello Mrs. {name}" }
- }
- }
- fn GreetingMale(cx: Scope) -> Element {
- let route = use_route(cx).unwrap();
- let name = route.parameter::<Name>()
- .map(|name| {
- let mut name = name.to_string();
- name.remove(0);
- name
- })
- .unwrap_or(String::from("Anonymous"));
- render! {
- p { "Hello Mr. {name}" }
- }
- }
- fn GreetingWithoutGender(cx: Scope) -> Element {
- let route = use_route(cx).unwrap();
- let name = route.parameter::<Name>()
- .map(|name| name.to_string())
- .unwrap_or(String::from("Anonymous"));
- render! {
- p { "Hello {name}" }
- }
- }
- fn GreetingKenobi(cx: Scope) -> Element {
- render! {
- p { "Hello there." }
- p { "General Kenobi." }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/fAnna").unwrap()),
- </span> ..Default::default()
- },
- &|| {
- Segment::empty()
- .fixed("kenobi", comp(GreetingKenobi))
- .matching(
- Regex::new("^f").unwrap(),
- ParameterRoute::content::<Name>(comp(GreetingFemale))
- )
- .matching(
- Regex::new("^m").unwrap(),
- (comp(GreetingMale), Name { })
- )
- .catch_all((comp(GreetingWithoutGender), Name { }))
- }
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(html, "<p>Hello Mrs. Anna</p>");
- </span><span class="boring">}
- </span></code></pre></pre>
- <h2 id="matcher"><a class="header" href="#matcher">Matcher</a></h2>
- <p>In the example above, both <em>matching routes</em> use a regular expression to specify
- when they match. However, <em>matching routes</em> are not limited to those. They
- accept all types that implement the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/trait.Matcher.html"><code>Matcher</code></a> trait.</p>
- <p>For example, you could (but probably shouldn't) implement a matcher, that
- matches all values with an even number of characters:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>#[derive(Debug)]
- struct EvenMatcher;
- impl Matcher for EvenMatcher {
- fn matches(&self, value: &str) -> bool {
- value.len() % 2 == 0
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="fallback-routes"><a class="header" href="#fallback-routes">Fallback Routes</a></h1>
- <p>Sometimes the router might be unable to find a route for the provided path. We
- might want it to show a prepared error message to our users in that case.
- Fallback routes allow us to do that.</p>
- <blockquote>
- <p>This is especially important for use cases where users can manually change the
- path, like web apps running in the browser.</p>
- </blockquote>
- <h2 id="a-single-global-fallback"><a class="header" href="#a-single-global-fallback">A single global fallback</a></h2>
- <p>To catch all cases of invalid paths within our app, we can simply add a fallback
- route to our root <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::{history::MemoryHistory, prelude::*};
- <span class="boring">extern crate dioxus_ssr;
- </span>
- fn Index(cx: Scope) -> Element {
- render! {
- h1 { "Index" }
- }
- }
- fn Fallback(cx: Scope) -> Element {
- render! {
- h1 { "Error 404 - Not Found" }
- p { "The page you asked for doesn't exist." }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/invalid").unwrap()),
- </span> ..Default::default()
- },
- &|| {
- Segment::content(comp(Index)).fallback(comp(Fallback))
- }
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> dioxus_ssr::render(&vdom),
- </span><span class="boring"> "<h1>Error 404 - Not Found</h1><p>The page you asked for doesn't exist.</p>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <h2 id="more-specific-fallback-routes"><a class="header" href="#more-specific-fallback-routes">More specific fallback routes</a></h2>
- <p>In some cases we might want to show different fallback content depending on what
- section of our app the user is in.</p>
- <p>For example, our app might have several settings pages under <code>/settings</code>, such
- as the password settings <code>/settings/password</code> or the privacy settings
- <code>/settings/privacy</code>. When our user is in the settings section, we want to show
- them <em>"settings not found"</em> instead of <em>"page not found"</em>.</p>
- <p>We can easily do that by setting a fallback route on our nested <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>. It
- will then replace the global fallback whenever our <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> was active.</p>
- <p>Note the <code>.clear_fallback(false)</code> part. If we didn't add this, the fallback
- content would be rendered inside the <code>Settings</code> component.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::{history::MemoryHistory, prelude::*};
- <span class="boring">extern crate dioxus_ssr;
- </span>
- // This example doesn't show the index or settings components. It only shows how
- // to set up several fallback routes.
- <span class="boring">fn Index(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Settings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn GeneralSettings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PasswordSettings(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PrivacySettings(cx: Scope) -> Element { unimplemented!() }
- </span>
- fn GlobalFallback(cx: Scope) -> Element {
- render! {
- h1 { "Error 404 - Page Not Found" }
- }
- }
- fn SettingsFallback(cx: Scope) -> Element {
- render! {
- h1 { "Error 404 - Settings Not Found" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/settings/invalid").unwrap()),
- </span> ..Default::default()
- },
- &|| {
- Segment::empty()
- .fixed("settings", Route::content(comp(Settings)).nested(
- Segment::content(comp(GeneralSettings))
- .fixed("password", comp(PasswordSettings))
- .fixed("privacy", comp(PrivacySettings))
- .fallback(comp(SettingsFallback))
- .clear_fallback(true)
- ))
- .fallback(comp(GlobalFallback))
- }
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> dioxus_ssr::render(&vdom),
- </span><span class="boring"> "<h1>Error 404 - Settings Not Found</h1>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="multiple-components--redirects"><a class="header" href="#multiple-components--redirects">Multiple Components & Redirects</a></h1>
- <h2 id="multiple-components"><a class="header" href="#multiple-components">Multiple Components</a></h2>
- <p>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 <a href="features/routes/../outlets.html#named-outlets">named <code>Outlet</code>s</a>.</p>
- <h2 id="redirects"><a class="header" href="#redirects">Redirects</a></h2>
- <p>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.</p>
- <blockquote>
- <p>Redirects to external pages only work in certain conditions. For more details
- see the chapter about <a href="features/routes/../failures/external.html">external navigation failures</a>.</p>
- </blockquote>
- <p>In the following example we will redirect everybody from <code>/</code> and <code>/start</code> to
- <code>/home</code>.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::{history::MemoryHistory, prelude::*};
- <span class="boring">extern crate dioxus_ssr;
- </span>
- fn Home(cx: Scope) -> Element {
- render! {
- h1 { "Home Page" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/home").unwrap()),
- </span> ..Default::default()
- },
- &|| {
- Segment::content(comp(Home))
- // notice that we use RouteContent::Redirect instead of
- // RouteContent::Content (which we have been using indirectly)
- .fixed(
- "home",
- RouteContent::Redirect(NavigationTarget::Internal("/".into()))
- )
- .fixed("start", "/") // short form
- });
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(html, "<h1>Home Page</h1>");
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="outlets"><a class="header" href="#outlets">Outlets</a></h1>
- <p><a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>s tell the router where to render content. In the following example
- the active routes content will be rendered within the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">extern crate dioxus_ssr;
- </span>
- fn Index(cx: Scope) -> Element {
- render! {
- h1 { "Index" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span> ..Default::default()
- },
- &|| Segment::content(comp(Index))
- );
- render! {
- header { "header" }
- Outlet { }
- footer { "footer" }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> html,
- </span><span class="boring"> "<header>header</header><h1>Index</h1><footer>footer</footer>"
- </span><span class="boring">);
- </span><span class="boring">}
- </span></code></pre></pre>
- <p>The example above will output the following HTML (line breaks added for
- readability):</p>
- <pre><code class="language-html"><header>
- header
- </header>
- <h1>
- Index
- </h1>
- <footer>
- footer
- </footer>
- </code></pre>
- <h2 id="nested-outlets"><a class="header" href="#nested-outlets">Nested Outlets</a></h2>
- <p>When using nested routes, we need to provide equally nested <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>s.</p>
- <blockquote>
- <p>Learn more about <a href="features/./routes/nested.html">nested routes</a> in their own chapter.</p>
- </blockquote>
- <h2 id="named-outlets"><a class="header" href="#named-outlets">Named Outlets</a></h2>
- <p>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.</p>
- <p>When defining our routes, we can use <code>RouteContentMulti</code> instead of
- <code>RouteContent::Component</code> (we've been using this through the <code>Into</code> trait) to
- tell the router about our content.</p>
- <p>We then can use a named <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> in our output, to tell the router where to
- put the side content.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">
- </span>
- 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 {
- <span class="boring"> synchronous: true,
- </span> ..Default::default()
- },
- &|| {
- Segment::content(
- multi(Some(comp(Main)))
- .add_named::<AsideName>(comp(Aside))
- )
- }
- );
- render! {
- Outlet { }
- Outlet {
- name: Name::of::<AsideName>()
- }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(html, "<main>Main Content</main><aside>Side Content</aside>");
- </span><span class="boring">}
- </span></code></pre></pre>
- <p>The example above will output the following HTML (line breaks added for
- readability):</p>
- <pre><code class="language-html"><main>
- Main Content
- </main>
- <aside>
- Side Content
- </aside>
- </code></pre>
- <h2 id="outlet-depth-override"><a class="header" href="#outlet-depth-override">Outlet depth override</a></h2>
- <p>When nesting <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>s, they communicate with each other. This allows the
- nested <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> to render the content of the nested route.</p>
- <p>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.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::{history::MemoryHistory, prelude::*};
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">
- </span>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 {
- <span class="boring"> synchronous: true,
- </span><span class="boring"> history: Box::new(MemoryHistory::with_initial_path("/root").unwrap()),
- </span> ..Default::default()
- },
- &|| {
- Segment::empty().fixed(
- "root",
- Route::content(comp(RootContent)).nested(
- Segment::content(comp(NestedContent))
- )
- )
- }
- );
- render! {
- Outlet {
- depth: 1
- }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(html, "<h2>Nested</h2>");
- </span><span class="boring">}
- </span></code></pre></pre>
- <p>The example above will output the following HTML (line breaks added for
- readability):</p>
- <pre><code class="language-html"><h2>
- Nested
- </h2>
- </code></pre>
- <h3 id="outlet-recursion"><a class="header" href="#outlet-recursion">Outlet recursion</a></h3>
- <p>This code will create a crash due to an unterminated recursion using
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>s.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>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 { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> in the <code>App</code> component has no parent <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>, so its depth
- will be <code>0</code>. When rendering for the path <code>/</code>, it therefore will render the
- <code>Content</code> component.</p>
- <p>The <code>Content</code> component will render an <code>h1</code> and an <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a>. That <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>OUtlet</code></a>
- would usually have a depth of <code>1</code>, since it is a descendant of the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> in
- the <code>App</code> component. However, we override its depth to <code>0</code>, so it will render
- the <code>Content</code> component.</p>
- <p>That means the <code>Content</code> component will recurse until someone (e.g. the OS) puts
- a stop to it.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="links--navigation"><a class="header" href="#links--navigation">Links & Navigation</a></h1>
- <p>When we split our app into pages, we need to provide our users with a way to
- navigate between them. On regular web pages we'd use an anchor element for that,
- like this:</p>
- <pre><code class="language-html"><a href="/other">Link to an other page</a>
- </code></pre>
- <p>However, we cannot do that when using the router for two reasons:</p>
- <ol>
- <li>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.</li>
- <li>Navigation using anchor tags only works when the app is running inside a
- browser. This means we cannot use them inside apps using Dioxus Desktop.</li>
- </ol>
- <p>To solve these problems, the router provides us with a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> component we can
- use like this:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span>fn SomeComponent(cx: Scope) -> Element {
- render! {
- Link {
- target: NavigationTarget::Internal(String::from("/some/path")),
- "Link text"
- }
- Link {
- target: "/some/path", // short form
- "Other link text"
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>The <code>target</code> in the example above is similar to the <code>href</code> of a regular anchor
- element. However, it tells the router more about what kind of navigation it
- should perform:</p>
- <ul>
- <li>The example uses <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal"><code>Internal</code></a>. We give it an arbitrary path that will be
- merged with the current URL.</li>
- <li><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Named"><code>Named</code></a> allows us to navigate within our app using predefined names.
- See the chapter about <a href="features/navigation/./name.html">named navigation</a> for more details.</li>
- <li><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a> allows us to navigate to URLs outside of our app. See the
- chapter about <a href="features/navigation/./external.html">external navigation</a> for more details.</li>
- </ul>
- <blockquote>
- <p>The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> accepts several props that modify its behavior. See the API docs
- for more details.</p>
- </blockquote>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="named-navigation"><a class="header" href="#named-navigation">Named Navigation</a></h1>
- <p>When creating large applications, it can become difficult to keep track of all
- routes and how to navigate to them. It also can be hard to find all links to
- them, which makes it difficult to change paths.</p>
- <p>To solve these problems, the router implements named navigation. When we define
- our routes we can give them arbitrary, unique names (completely independent from
- the path) and later ask the router to navigate to those names. The router will
- automatically create the actual path to navigate to, even inserting required
- parameters.</p>
- <p><em>Named</em> navigation has a few advantages over <em>path-based</em> navigation:</p>
- <ul>
- <li>Links can be created without knowing the actual path.</li>
- <li>It is much easier to find all links to a specific route.</li>
- <li>The router knows what links are invalid (and will panic in debug builds).</li>
- </ul>
- <blockquote>
- <p>When the router encounters an invalid link in a release build, it has to
- handle that problem. You can hook into that process, to display a custom error
- message. See the chapter about
- <a href="features/navigation/../failures/named.html">named navigation failures</a>.</p>
- </blockquote>
- <blockquote>
- <p>The router will automatically define the name <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.RootIndex.html"><code>RootIndex</code></a> to refer to the
- root index route (<code>/</code>).</p>
- <p>It will also add other names (all of them are in the prelude module) in
- certain conditions. None of these names can be used for app defined routes.</p>
- </blockquote>
- <h2 id="code-example-1"><a class="header" href="#code-example-1">Code Example</a></h2>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- <span class="boring">extern crate dioxus_ssr;
- </span>
- // we define a unit struct which will serve as our name
- struct TargetName;
- fn Source(cx: Scope) -> Element {
- render! {
- Link {
- // instead of InternalTarget we use NamedTarget (via the `named` fn)
- // we can use the returned value to add parameters or a query
- target: named::<TargetName>().query("query"),
- "Go to target"
- }
- }
- }
- fn Target(cx: Scope) -> Element {
- render! {
- h1 { "Target" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span> ..Default::default()
- },
- &|| {
- Segment::content(comp(Source))
- .fixed(
- "target_path",
- Route::content(comp(Target)).name::<TargetName>()
- )
- }
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> html,
- </span><span class="boring"> format!(
- </span><span class="boring"> "<a {attr1} {attr2}>Go to target</a>",
- </span><span class="boring"> attr1 = r#"href="/target_path?query" dioxus-prevent-default="onclick""#,
- </span><span class="boring"> attr2 = r#"class="" id="" rel="" target="""#
- </span><span class="boring"> )
- </span><span class="boring">)
- </span><span class="boring">}
- </span></code></pre></pre>
- <h2 id="check-if-a-name-is-present"><a class="header" href="#check-if-a-name-is-present">Check if a name is present</a></h2>
- <p>You can check if a specific name is present for the current route. This works
- similar to getting the value of a <a href="features/navigation/../routes/parameter.html">parameter route</a> and
- the same restrictions apply.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- struct SomeName;
- fn Content(cx: Scope) -> Element {
- let route = use_route(cx).expect("needs to be in router");
- if route.is_at(&named::<SomeName>(), false) {
- // do something
- }
- // ...
- <span class="boring"> todo!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="external-navigation"><a class="header" href="#external-navigation">External Navigation</a></h1>
- <p>In modern apps, and especially on the web, we often want to send our users to an
- other website. <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a> allows us to make a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> navigate to an
- external page.</p>
- <blockquote>
- <p>You might already now about
- <a href="features/navigation/../failures/external.html">external navigation failures</a>. The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a>
- component doesn't rely on the code path where those originate. Therefore a
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> will never trigger an external navigation failure.</p>
- </blockquote>
- <p>Strictly speaking, a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> is not necessary for navigating to external
- targets, since by definition the router cannot handle them internally. However,
- the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> component is more convenient to use, as it automatically sets the
- <code>rel</code> attribute for the link, when the target is external.</p>
- <h2 id="code-example-2"><a class="header" href="#code-example-2">Code Example</a></h2>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- <span class="boring">extern crate dioxus_ssr;
- </span>
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span> ..Default::default()
- },
- &|| Segment::empty()
- );
- render! {
- // links need to be inside a router, even if they navigate to an
- // external page
- Link {
- target: NavigationTarget::External("https://dioxuslabs.com/".into()),
- "Go to the dioxus home page"
- }
- Link {
- target: "https://dioxuslabs.com/", // short form
- "Go to the dioxus home page 2"
- }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">let html = dioxus_ssr::render(&vdom);
- </span><span class="boring">assert_eq!(
- </span><span class="boring"> html,
- </span><span class="boring"> format!(
- </span><span class="boring"> "<a {attr1} {attr2}>{text}</a><a {attr1} {attr2}>{text} 2</a>",
- </span><span class="boring"> attr1 = r#"href="https://dioxuslabs.com/" dioxus-prevent-default="""#,
- </span><span class="boring"> attr2 = r#"class="" id="" rel="noopener noreferrer" target="""#,
- </span><span class="boring"> text = "Go to the dioxus home page"
- </span><span class="boring"> )
- </span><span class="boring">)
- </span><span class="boring">}
- </span></code></pre></pre>
- <blockquote>
- <p>Note that the short form for an [<code>ExternalTarget</code>] looks like the short form
- for an [<code>InternalTarget</code>]. The router will create an [<code>ExternalTarget</code>] only
- if the URL is absolute.</p>
- </blockquote>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="programmatic-navigation"><a class="header" href="#programmatic-navigation">Programmatic Navigation</a></h1>
- <p>Sometimes we want our application to navigate to another page without having the
- user click on a link. This is called programmatic navigation.</p>
- <h2 id="acquiring-a-navigator"><a class="header" href="#acquiring-a-navigator">Acquiring a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/hooks/struct.Navigator.html"><code>Navigator</code></a></a></h2>
- <p>To use programmatic navigation, we first have to acquire a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/hooks/struct.Navigator.html"><code>Navigator</code></a>. For
- that purpose we can use the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_navigate.html"><code>use_navigate</code></a> hook.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- fn Content(cx: Scope) -> Element {
- let nav = use_navigate(cx).expect("called inside a router");
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="triggering-a-navigation"><a class="header" href="#triggering-a-navigation">Triggering a Navigation</a></h2>
- <p>We can use the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/hooks/struct.Navigator.html"><code>Navigator</code></a> to trigger four different kinds of navigation:</p>
- <ul>
- <li><code>push</code> will navigate to the target. It works like a regular anchor tag.</li>
- <li><code>replace</code> works like <code>push</code>, except that it replaces the current history entry
- instead of adding a new one. This means the prior page cannot be restored with
- the browsers back button.</li>
- <li><code>Go back</code> works like the browsers back button.</li>
- <li><code>Go forward</code> works like the browsers forward button (the opposite of the back
- button).</li>
- </ul>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>fn Content(cx: Scope) -> Element {
- let nav = use_navigate(cx).expect("called inside a router");
- // push
- nav.push("/target");
- // replace
- nav.replace("/target");
- // go back
- nav.go_back();
- // go forward
- nav.go_forward();
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <p>You might have noticed that, like <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a>, the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/hooks/struct.Navigator.html"><code>Navigator</code></a>s <code>push</code> and
- <code>replace</code> functions take a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html"><code>NavigationTarget</code></a>. This means we can use
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal"><code>Internal</code></a>, <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Named"><code>Named</code></a> and <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a>.</p>
- <h2 id="external-navigation-targets"><a class="header" href="#external-navigation-targets">External Navigation Targets</a></h2>
- <p>Unlike a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a>, the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/hooks/struct.Navigator.html"><code>Navigator</code></a> cannot rely on the browser (or webview) to
- handle navigation to external targets via a generated anchor element.</p>
- <p>This means, that under certain conditions, navigation to external targets can
- fail. See the chapter about
- <a href="features/navigation/../failures/external.html">external navigation failures</a> for more details.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="query"><a class="header" href="#query">Query</a></h1>
- <p>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.</p>
- <h2 id="accessing-the-query"><a class="header" href="#accessing-the-query">Accessing the query</a></h2>
- <p>The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_route.html"><code>use_route</code></a> hook allows us to access the current query in two ways. The
- returned <code>struct</code> contains a <code>query</code> field, that contains the query (without the
- leading <code>?</code>).</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- fn SomeComponent(cx: Scope) -> Element {
- let route = use_route(cx).expect("nested in Router");
- let query = route.query.clone().unwrap();
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="setting-the-query"><a class="header" href="#setting-the-query">Setting the query</a></h2>
- <p>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.</p>
- <h3 id="internal-and-external"><a class="header" href="#internal-and-external"><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal"><code>Internal</code></a> and <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a></a></h3>
- <p>When using <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal"><code>Internal</code></a> or <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a> we have to append our query manually.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>fn SomeComponent(cx: Scope) -> Element {
- render! {
- Link {
- target: NavigationTarget::Internal("/some/path?query=yes".into()),
- "Internal target"
- }
- Link {
- target: NavigationTarget::External("https://dioxuslab.com?query=yes".into()),
- "External target"
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <h3 id="named"><a class="header" href="#named"><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Named"><code>Named</code></a></a></h3>
- <p>When using <a href="features/./navigation/name.html">named navigation</a> we can pass the query via
- a function.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">struct Target;
- </span><span class="boring">
- </span>fn SomeComponent(cx: Scope) -> Element {
- render! {
- Link {
- target: named::<Target>().query("query=yes"),
- "Query String"
- }
- Link {
- target: named::<Target>().query(vec![("query", "yes")]),
- "Query Vec"
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="navigation-failures"><a class="header" href="#navigation-failures">Navigation Failures</a></h1>
- <p>Some specific operations can cause a failure within router operations. The
- subchapters contain information on how the router lets us handle such failures.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="named-navigation-failure"><a class="header" href="#named-navigation-failure">Named Navigation Failure</a></h1>
- <p>When using <a href="features/failures/../navigation/name.html">named navigation</a>, the router runs into a
- problem under these circumstances:</p>
- <ol>
- <li>The name we try to navigate to is not contained within our routes.</li>
- <li>The route we navigate to requires a parameter that we don't provide when
- triggering the navigation.</li>
- </ol>
- <blockquote>
- <p>Users cannot directly interact with named navigation. If a named navigation
- failure occurs, your app (or the router) has a bug.</p>
- </blockquote>
- <p>The router reacts to this problem differently, depending on our apps build kind.</p>
- <h2 id="debug"><a class="header" href="#debug">Debug</a></h2>
- <p>When running a debug build, the router will <code>panic</code> whenever it encounters an
- invalid navigation. This ensures that we notice these problems when we are
- testing our application.</p>
- <h2 id="release"><a class="header" href="#release">Release</a></h2>
- <p>When running a release build, the router can't just <code>panic</code>, as that would be a
- horrible user experience. Instead, it changes to show some fallback content.</p>
- <blockquote>
- <p>You can detect if the router is in the named navigation failure handling state
- by <a href="features/failures/../navigation/name.html#check-if-a-name-is-present">checking</a> if the
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.FailureNamedNavigation.html"><code>FailureNamedNavigation</code></a> name is present.</p>
- </blockquote>
- <p>The default fallback explains to the user that an error occurred and asks them
- to report the bug to the app developer.</p>
- <p>You can override it by setting the <code>failure_named_navigation</code> value of the
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/struct.RouterConfiguration.html"><code>RouterConfiguration</code></a>.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span>fn NamedNavigationFallback(cx: Scope) -> Element {
- render! {
- h1 { "Named navigation failure!" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- failure_named_navigation: comp(NamedNavigationFallback),
- ..Default::default()
- },
- &|| Segment::empty()
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="external-navigation-failure"><a class="header" href="#external-navigation-failure">External Navigation Failure</a></h1>
- <blockquote>
- <p>This section doesn't apply when specifying a <code>target</code> on a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a>. See the
- chapter about <a href="features/failures/../navigation/external.html">external navigation</a> for more
- details.</p>
- </blockquote>
- <p>When we ask the router to navigate to an external target, either through
- <a href="features/failures/../navigation/programmatic.html">programmatic navigation</a> or a
- <a href="features/failures/../routes/multiple-and-redirect.html#redirects">redirect</a> the router needs to
- navigate to an external target without being able to rely on an anchor element.</p>
- <p>This will only work in the browser, when using either <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHistory.html"><code>WebHistory</code></a> or
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHashHistory.html"><code>WebHashHistory</code></a>.</p>
- <h2 id="failure-handling"><a class="header" href="#failure-handling">Failure handling</a></h2>
- <p>When the router encounters an external navigation it cannot fulfill, it changes
- the path to <code>/</code> and shows some fallback content.</p>
- <blockquote>
- <p>You can detect if the router is in the external navigation failure handling
- state by <a href="features/failures/../navigation/name.html#check-if-a-name-is-present">checking</a> if the
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.FailureExternalNavigation.html"><code>FailureExternalNavigation</code></a> name is present.</p>
- </blockquote>
- <p>The default fallback explains to the user that the navigation was unsuccessful
- and provides them with a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> to fulfill it manually. It also allows them to
- go back to the previous page.</p>
- <p>You can override it by setting the <code>failure_external_navigation</code> value of the
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/struct.RouterConfiguration.html"><code>RouterConfiguration</code></a>. The external URL will be provided via the
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.FailureExternalNavigation.html"><code>FailureExternalNavigation</code></a> parameter.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span>fn ExternalNavigationFallback(cx: Scope) -> Element {
- let route = use_route(cx).expect("is nested within a Router component");
- let url = route
- .parameter::<FailureExternalNavigation>()
- .unwrap_or_default();
- render! {
- h1 { "External navigation failure!" }
- Link {
- target: url,
- "Go to external site"
- }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- failure_external_navigation: comp(ExternalNavigationFallback),
- ..Default::default()
- },
- &|| Segment::empty()
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="redirection-limit-failure"><a class="header" href="#redirection-limit-failure">Redirection Limit Failure</a></h1>
- <p>The router enforces a limit of 25 redirects during a single routing navigation.
- This is done to prevent infinite loops. If your app breaches that limit, you
- should reorganize its routes to reduce the number of redirects.</p>
- <blockquote>
- <p>Users cannot trigger a redirection. If the redirection limit is breached, your
- app (or the router) has a bug.</p>
- </blockquote>
- <blockquote>
- <p>The <a href="features/failures/../routing-update-callback.html"><code>on_update</code></a> callback doesn't count
- towards the limit, and resets it. You may have 25 redirects, then add an other
- one via the callback, and then have another 25.</p>
- </blockquote>
- <p>The router reacts to a breach differently, depending on our apps build kind.</p>
- <h2 id="debug-1"><a class="header" href="#debug-1">Debug</a></h2>
- <p>When running a debug build, the router will <code>panic</code> whenever the redirecion
- limit is breached. This ensures that we notice these problems when we are
- testing our application.</p>
- <h2 id="release-1"><a class="header" href="#release-1">Release</a></h2>
- <p>When running a release build, the router can't just <code>panic</code>, as that would be a
- horrible user experience. Instead, it changes to show some fallback content.</p>
- <blockquote>
- <p>You can detect if the router is in the redirection limit failure handling
- state by <a href="features/failures/../navigation/name.html#check-if-a-name-is-present">checking</a> if the
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.FailureRedirectionLimit.html"><code>FailureRedirectionLimit</code></a> name is present.</p>
- </blockquote>
- <p>The default fallback explains to the user that an error occurred and asks them
- to report the bug to the app developer.</p>
- <p>You can override it by setting the <code>failure_redirection_limit</code> value of the
- <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/struct.RouterConfiguration.html"><code>RouterConfiguration</code></a>.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span>fn RedirectionLimitFallback(cx: Scope) -> Element {
- render! {
- h1 { "Redirection limit breached!" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- failure_redirection_limit: comp(RedirectionLimitFallback),
- ..Default::default()
- },
- &|| Segment::empty()
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="history-providers"><a class="header" href="#history-providers">History Providers</a></h1>
- <p>In order to provide the ability to traverse the navigation history, the router
- uses <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/trait.HistoryProvider.html"><code>HistoryProvider</code></a>s. Those implement the actual back-and-forth
- functionality.</p>
- <p>The router provides five <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/trait.HistoryProvider.html"><code>HistoryProvider</code></a>s, but you can also create your own.
- The five default implementations are:</p>
- <ul>
- <li>The <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.MemoryHistory.html"><code>MemoryHistory</code></a> is a custom implementation that works in memory.</li>
- <li>The <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHistory.html"><code>WebHistory</code></a> integrates with the browsers URL.</li>
- <li>The <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHashHistory.html"><code>WebHashHistory</code></a> also integrates with the browser, but uses the fragment
- part of the URL.</li>
- </ul>
- <p>By default the router uses the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.MemoryHistory.html"><code>MemoryHistory</code></a>. It might be changed to use
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHistory.html"><code>WebHistory</code></a> when the <code>web</code> feature is active, but that is not guaranteed.</p>
- <p>You can override the default history:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::{prelude::*, history::WebHashHistory};
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- history: Box::new(WebHashHistory::new(true)),
- ..Default::default()
- },
- &|| Segment::empty()
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="history-buttons"><a class="header" href="#history-buttons">History Buttons</a></h1>
- <p>Some platforms, like web browsers, provide users with an easy way to navigate
- through an apps history. They have UI elements or integrate with the OS.</p>
- <p>However, native platforms usually don't provide such amenities, which means that
- apps wanting users to have access to them, need to implement them. For this
- reason the router comes with two components, which emulate a browsers back and
- forward buttons:</p>
- <ul>
- <li><a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.GoBackButton.html"><code>GoBackButton</code></a></li>
- <li><a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.GoForwardButton.html"><code>GoForwardButton</code></a></li>
- </ul>
- <blockquote>
- <p>If you want to navigate through the history programmatically, take a look at
- <a href="features/./navigation/programmatic.html"><code>programmatic navigation</code></a>.</p>
- </blockquote>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- fn HistoryNavigation(cx: Scope) -> Element {
- render! {
- GoBackButton {
- "Back to the Past"
- }
- GoForwardButton {
- "Back to the Future" /* You see what I did there? 😉 */
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>As you might know, browsers usually disable the back and forward buttons if
- there is no history to navigate to. The routers history buttons try to do that
- too, but depending on the <a href="features/./history-providers.html">history provider</a> that might not be possible.</p>
- <p>Importantly, neither <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHistory.html"><code>WebHistory</code></a> nor <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHashHistory.html"><code>WebHashHistory</code></a> support that feature.
- This is due to limitations of the browser History API.</p>
- <p>However, in both cases the router will just ignore button presses, if there is
- no history to navigate to.</p>
- <p>Also, when using <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHistory.html"><code>WebHistory</code></a> or <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/history/struct.WebHashHistory.html"><code>WebHashHistory</code></a>, the history buttons might
- navigate a user to a history entry outside your app.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="sitemap-generation"><a class="header" href="#sitemap-generation">Sitemap Generation</a></h1>
- <p>If you need a list of all routes you have defined (e.g. for statically
- generating all pages), Dioxus Router provides functions to extract that
- information from a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>.</p>
- <h2 id="preparing-an-app"><a class="header" href="#preparing-an-app">Preparing an app</a></h2>
- <p>We will start by preparing an app with some routes like we normally would.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::{history::MemoryHistory, prelude::*};
- <span class="boring">extern crate dioxus_ssr;
- </span>
- fn Home(cx: Scope) -> Element {
- render! {
- h1 { "Home" }
- }
- }
- fn Fixed(cx: Scope) -> Element {
- render! {
- h1 { "Fixed" }
- Outlet { }
- }
- }
- fn Nested(cx: Scope) -> Element {
- render! {
- h2 { "Nested" }
- }
- }
- struct ParameterName;
- fn Parameter(cx: Scope) -> Element {
- let route = use_route(cx).unwrap();
- let param = route.parameter::<ParameterName>().unwrap_or_default();
- render! {
- h1 { "Parameter: {param}" }
- }
- }
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span> history: Box::new(MemoryHistory::with_initial_path("/fixed/nested").unwrap()),
- ..Default::default()
- },
- &|| {
- Segment::content(comp(Home))
- .fixed(
- "fixed",
- Route::content(comp(Fixed)).nested(
- Segment::empty().fixed("nested", comp(Nested))
- )
- )
- .catch_all((comp(Parameter), ParameterName { }))
- }
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(dioxus_ssr::render(&mut vdom), "<h1>Fixed</h1><h2>Nested</h2>");
- </span><span class="boring">}
- </span></code></pre></pre>
- <h2 id="modifying-the-app-to-make-using-sitemaps-easier"><a class="header" href="#modifying-the-app-to-make-using-sitemaps-easier">Modifying the app to make using sitemaps easier</a></h2>
- <p>Preparing our app for sitemap generation is quite easy. We just need to extract
- our segment definition into its own function.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Fixed(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Nested(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">struct ParameterName;
- </span><span class="boring">fn Parameter(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- ..Default::default()
- },
- &prepare_routes
- );
- render! {
- Outlet { }
- }
- }
- fn prepare_routes() -> Segment<Component> {
- Segment::content(comp(Home))
- .fixed(
- "fixed",
- Route::content(comp(Fixed)).nested(
- Segment::empty().fixed("nested", comp(Nested))
- )
- )
- .catch_all((comp(Parameter), ParameterName { }))
- }
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="sitemaps-with-parameter-names"><a class="header" href="#sitemaps-with-parameter-names">Sitemaps with parameter names</a></h2>
- <p>The first variant to generate sitemaps is very simple. It finds all routes
- within the <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a> and adds them to the returned <code>Vec</code>.</p>
- <p>Matching and parameter routes are represented by their <code>key</code>, prefixed with <code>\</code>.
- Besides that <code>\</code>, all paths are URL encoded.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Fixed(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Nested(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">struct ParameterName;
- </span><span class="boring">fn Parameter(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn prepare_routes() -> Segment<Component> {
- </span><span class="boring"> Segment::content(comp(Home))
- </span><span class="boring"> .fixed(
- </span><span class="boring"> "fixed",
- </span><span class="boring"> Route::content(comp(Fixed)).nested(
- </span><span class="boring"> Segment::empty().fixed("nested", comp(Nested))
- </span><span class="boring"> )
- </span><span class="boring"> )
- </span><span class="boring"> .catch_all((comp(Parameter), ParameterName { }))
- </span><span class="boring">}
- </span>
- let expected = vec![
- "/",
- "/fixed",
- "/fixed/nested",
- // Usually, here would be a fourth result representing the parameter route.
- // However, due to mdbook the name for this file would constantly change,
- // which is why we cannot show it. It would look something like this:
- // "/\\your_crate::ParameterName",
- ];
- let mut sitemap = prepare_routes().gen_sitemap();
- sitemap.remove(3); // see above
- assert_eq!(sitemap, expected);
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="sitemaps-with-actual-parameter-values"><a class="header" href="#sitemaps-with-actual-parameter-values">Sitemaps with actual parameter values</a></h2>
- <p>The second variant to generate sitemaps is a bit more involved. When it
- encounters a parameter route, it inserts all values with a matching <code>key</code> that
- were provided to it.</p>
- <p>Matching routes only add their path if the value matches their regex.</p>
- <p>All paths are URL encoded.</p>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span>use std::collections::{BTreeMap, HashSet};
- <span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Fixed(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Nested(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">struct ParameterName;
- </span><span class="boring">fn Parameter(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn prepare_routes() -> Segment<Component> {
- </span><span class="boring"> Segment::content(comp(Home))
- </span><span class="boring"> .fixed(
- </span><span class="boring"> "fixed",
- </span><span class="boring"> Route::content(comp(Fixed)).nested(
- </span><span class="boring"> Segment::empty().fixed("nested", comp(Nested))
- </span><span class="boring"> )
- </span><span class="boring"> )
- </span><span class="boring"> .catch_all((comp(Parameter), ParameterName { }))
- </span><span class="boring">}
- </span>
- let parameters = {
- let mut parameters = BTreeMap::new();
- parameters.insert(
- Name::of::<ParameterName>(),
- vec![
- String::from("some-parameter-value"),
- String::from("other-parameter-value")
- ]
- );
- parameters
- };
- let expected: Vec<String> = vec![
- "/",
- "/fixed",
- "/fixed/nested",
- "/some-parameter-value",
- "/other-parameter-value",
- ].into_iter().map(String::from).collect();
- assert_eq!(expected, prepare_routes().gen_parameter_sitemap(&parameters));
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="routing-update-callback"><a class="header" href="#routing-update-callback">Routing Update Callback</a></h1>
- <p>In some cases we might want to run custom code when the current route changes.
- For this reason, the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/struct.RouterConfiguration.html"><code>RouterConfiguration</code></a> exposes an <code>on_update</code> field.</p>
- <h2 id="how-does-the-callback-behave"><a class="header" href="#how-does-the-callback-behave">How does the callback behave?</a></h2>
- <p>The <code>on_update</code> 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.</p>
- <p>If the callback returns a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html"><code>NavigationTarget</code></a>, the router will replace the
- current location with the specified target. It will not call the
- <code>on_update</code> again.</p>
- <p>If at any point the router encounters a
- <a href="features/./failures/index.html">navigation failure</a>, it will go to the appropriate state
- without calling the <code>on_update</code>. It doesn't matter if the invalid target
- initiated the navigation, was found as a redirect target or returned by the
- <code>on_update</code> itself.</p>
- <h2 id="code-example-3"><a class="header" href="#code-example-3">Code Example</a></h2>
- <pre><pre class="playground"><code class="language-rust edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">extern crate dioxus_ssr;
- </span><span class="boring">
- </span>use std::sync::Arc;
- use dioxus::prelude::*;
- use dioxus_router::prelude::*;
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration {
- <span class="boring"> synchronous: true,
- </span> on_update: Some(Arc::new(|state| -> Option<NavigationTarget> {
- if state.path == "/" {
- return Some("/home".into());
- }
- None
- })),
- ..Default::default()
- },
- &|| Segment::empty().fixed("home", comp(Content))
- );
- render! {
- Outlet { }
- }
- }
- fn Content(cx: Scope) -> Element {
- render! {
- p { "Some content" }
- }
- }
- <span class="boring">
- </span><span class="boring">let mut vdom = VirtualDom::new(App);
- </span><span class="boring">vdom.rebuild();
- </span><span class="boring">assert_eq!(dioxus_ssr::render(&mut vdom), "<p>Some content</p>");
- </span><span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="overview"><a class="header" href="#overview">Overview</a></h1>
- <p>In this guide you'll learn to effectively use Dioxus Router whether you're
- building a small todo app or the next FAANG company. We will create a small
- website with a blog, homepage, and more!</p>
- <blockquote>
- <p>To follow along with the router example, you'll need a working Dioxus app.
- Check out the <a href="https://dioxuslabs.com/guide/">Dioxus book</a> to get started.</p>
- </blockquote>
- <blockquote>
- <p>Make sure to add Dioxus Router as a dependency, as explained in the
- <a href="example/../index.html">introduction</a>.</p>
- </blockquote>
- <h2 id="youll-learn-how-to"><a class="header" href="#youll-learn-how-to">You'll learn how to</a></h2>
- <ul>
- <li>Create routes and render "pages".</li>
- <li>Utilize nested routes, create a navigation bar, and render content for a
- set of routes.</li>
- <li>Gather URL parameters to dynamically display content.</li>
- <li>Redirect your visitors wherever you want.</li>
- </ul>
- <blockquote>
- <p><strong>Disclaimer</strong></p>
- <p>The example will only display the features of Dioxus Router. It will not
- include any actual functionality. To keep things simple we will only be using
- a single file, this is not the recommended way of doing things with a real
- application.</p>
- </blockquote>
- <p>You can find the complete application in the <a href="example/./full-code.html">full code</a>
- chapter.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="creating-our-first-route"><a class="header" href="#creating-our-first-route">Creating Our First Route</a></h1>
- <p>In this chapter, we will start utilizing Dioxus Router and add a homepage and a
- 404 page to our project.</p>
- <h2 id="fundamentals"><a class="header" href="#fundamentals">Fundamentals</a></h2>
- <p>Dioxus Router works based on a <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a> hook, a route definition in pure
- rust and <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Outlet.html"><code>Outlet</code></a> components. If you've ever used <a href="https://router.vuejs.org/">Vue Router</a>, you should
- feel right at home with Dioxus Router.</p>
- <p>First we need an actual page to route to! Let's add a homepage component:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">
- </span>fn Home(cx: Scope) -> Element {
- render! {
- h1 { "Welcome to the Dioxus Blog!" }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <h2 id="to-route-or-not-to-route"><a class="header" href="#to-route-or-not-to-route">To Route or Not to Route</a></h2>
- <p>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.</p>
- <p>To start using Dioxus Router, we need to use the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a> hook. All other
- hooks and components the router provides can only be used as a descendant of a
- component calling <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a>.</p>
- <p>The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a> hook takes three arguments:</p>
- <ol>
- <li><code>cx</code>, which is a common argument for all hooks.</li>
- <li>A <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/struct.RouterConfiguration.html"><code>RouterConfiguration</code></a>, which allows us to modify its behavior.</li>
- <li>A definition of all routes the application contains, in the form of its root
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/routes/struct.Segment.html"><code>Segment</code></a>.</li>
- </ol>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span>use dioxus::prelude::*;
- <span class="boring">extern crate dioxus_router;
- </span>use dioxus_router::prelude::*;
- <span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span>
- fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration::default(),
- &|| Segment::content(comp(Home))
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>If you head to your application's browser tab, you should now see the text
- <code>Welcome to Dioxus Blog!</code> when on the root URL (<code>http://localhost:8080/</code>). If
- you enter a different path for the URL, nothing should be displayed.</p>
- <p>This is because we told Dioxus Router to render the <code>Home</code> component only when
- the URL path is <code>/</code>. The <em>index</em> (<code>Segment::content()</code>) functionality we used
- basically emulates how web servers treat <code>index.html</code> files.</p>
- <h2 id="what-if-a-route-doesnt-exist"><a class="header" href="#what-if-a-route-doesnt-exist">What if a Route Doesn't Exist?</a></h2>
- <p>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!</p>
- <p>First, we create a new <code>PageNotFound</code> component.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">
- </span>fn PageNotFound(cx: Scope) -> Element {
- render! {
- h1 { "Page not found" }
- p { "We are terribly sorry, but the page you requested doesn't exist." }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>Now to tell Dioxus Router to render our new component when no route exists.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration::default(),
- &|| {
- Segment::content(comp(Home))
- .fallback(comp(PageNotFound)) // this is new
- }
- );
- render! {
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>Now when you go to a route that doesn't exist, you should see the page not found
- text.</p>
- <h2 id="conclusion"><a class="header" href="#conclusion">Conclusion</a></h2>
- <p>In this chapter we learned how to create a route and tell Dioxus Router what
- component to render when the URL path is <code>/</code>. 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.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="building-a-nest"><a class="header" href="#building-a-nest">Building a Nest</a></h1>
- <p>Not a bird's nest! A nest of routes!</p>
- <p>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 <a href="https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html"><code>use_router</code></a>.</p>
- <h2 id="site-navigation"><a class="header" href="#site-navigation">Site Navigation</a></h2>
- <p>Our site visitors won't know all the available pages and blogs on our site so we
- should provide a navigation bar for them.
- Let's create a new <code>NavBar</code> component:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>fn NavBar(cx: Scope) -> Element {
- render! {
- nav {
- ul { }
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>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 <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> component provided by Dioxus Router.</p>
- <p>The <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> is similar to a regular <code>a</code> tag. It takes a target (for now a path,
- more on other targets later) and an element. Let's add our links</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>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
- }
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <blockquote>
- <p>Using this method, the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> component only works for links within our
- application. To learn more about navigation targets see
- <a href="example/./navigation-targets.html">here</a>.</p>
- </blockquote>
- <p>And finally, we add the navbar component in our app component:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn NavBar(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>fn App(cx: Scope) -> Element {
- use_router(
- cx,
- &|| RouterConfiguration::default(),
- &|| Segment::content(comp(Home)).fallback(comp(PageNotFound))
- );
- render! {
- NavBar { } // this is new
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>Now you should see a list of links near the top of your page. Click on one and
- you should seamlessly travel between pages.</p>
- <h3 id="active-link-styling"><a class="header" href="#active-link-styling">Active Link Styling</a></h3>
- <p>You might want to style links differently, when their page is currently open.
- To achieve this, we can tell the <a href="https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html"><code>Link</code></a> to give its internal <code>a</code> tag a class
- in that case.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>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"
- } }
- }
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <blockquote>
- <p>This will not be reflected in the <a href="example/./full-code.html">full example code</a>.</p>
- </blockquote>
- <h2 id="url-parameters-and-nested-routes"><a class="header" href="#url-parameters-and-nested-routes">URL Parameters and Nested Routes</a></h2>
- <p>Many websites such as GitHub put parameters in their URL. For example,
- <code>https://github.com/DioxusLabs</code> utilizes the text after the domain to
- dynamically search and display content about an organization.</p>
- <p>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
- want our users to be able to send people a link to a specific blog post.</p>
- <p>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.</p>
- <p>The path to our blog will look like <code>/blog/myBlogPage</code>, <code>myBlogPage</code> being the
- URL parameter.</p>
- <p>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.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>fn Blog(cx: Scope) -> Element {
- render! {
- h1 { "Blog" }
- Outlet {}
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <blockquote>
- <p>Note the <code>Outlet { }</code> component. For the components of a nested route to be
- rendered, we need an equally nested outlet. For more details, see the
- <a href="example/../features/routes/nested.html">nested routes</a> chapter of the features section.</p>
- </blockquote>
- <p>Now we'll create another index component, that'll be displayed when no blog post
- is selected:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>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"
- } }
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>We also need to create a component that displays an actual blog post. Within
- this component we can use the <code>use_route</code> hook to gain access to our URL
- parameters:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>struct PostId;
- 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}"}
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>Finally, let's tell our router about those components.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Blog(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn BlogList(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">struct PostId;
- </span><span class="boring">fn BlogPost(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn NavBar(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>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))
- }
- );
- render! {
- NavBar { }
- Outlet { }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>That's it! If you head to <code>/blog/1</code> you should see our sample post.</p>
- <h2 id="conclusion-1"><a class="header" href="#conclusion-1">Conclusion</a></h2>
- <p>In this chapter we utilized Dioxus Router's Link, URL Parameter, and <code>use_route</code>
- 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.</p>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="navigation-targets"><a class="header" href="#navigation-targets">Navigation Targets</a></h1>
- <p>In the previous chapter we learned how to create links to pages within our app.
- We told them where to go using the <code>target</code> property. This property takes a
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html"><code>NavigationTarget</code></a>.</p>
- <h2 id="what-is-a-navigation-target"><a class="header" href="#what-is-a-navigation-target">What is a navigation target?</a></h2>
- <p>A <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html"><code>NavigationTarget</code></a> is similar to the <code>href</code> of an HTML anchor element.It
- tells the router where to navigate to. The Dioxus Router knows three kinds of
- navigation targets:</p>
- <ul>
- <li><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal"><code>Internal</code></a>: we already saw that. It's basically an <code>href</code>, but cannot
- link to content outside our app.</li>
- <li><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a>: This works exactly like an HTML anchors <code>href</code>. 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.</li>
- <li><a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Named"><code>Named</code></a>: this is the most interesting form of navigation target. We'll look
- at it in detail in this chapter.</li>
- </ul>
- <h2 id="external-navigation-1"><a class="header" href="#external-navigation-1">External navigation</a></h2>
- <p>If we need a link to an external page we can do it like this:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>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"
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <blockquote>
- <p>Note that we can use a <code>str</code>, just like with <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Internal"><code>Internal</code></a>s. The router will
- convert a <code>str</code> to an <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.External"><code>External</code></a> if the URL is absolute.</p>
- </blockquote>
- <h2 id="named-navigation-1"><a class="header" href="#named-navigation-1">Named navigation</a></h2>
- <p>When defining our routes, we can optionally give them unique static names. This
- is required for a feature we call named navigation.</p>
- <p>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.</p>
- <p>This has several advantages:</p>
- <ul>
- <li>We don't have to remember absolute paths or care about what the current path
- is.</li>
- <li>Changing paths later on won't break internal links.</li>
- <li>Paths can easily be localized without affecting app logic.</li>
- <li>The compiler makes sure we don't have typos.</li>
- </ul>
- <p>Let's try that now! First, we give our blog post route a name. We can reuse our
- <code>BlogPost</code> component as a name.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Blog(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn BlogList(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">struct PostId;
- </span><span class="boring">fn BlogPost(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>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))
- }
- );
- // ...
- <span class="boring"> unimplemented!()
- </span>}
- <span class="boring">}
- </span></code></pre></pre>
- <p>Now we can change the targets of the links in our <code>BlogList</code> component.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">struct PostId;
- </span><span class="boring">struct BlogPostName;
- </span><span class="boring">fn BlogPost(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">
- </span>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"
- } }
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <p>As you can see, a <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/navigation/enum.NavigationTarget.html#variant.Named"><code>Named</code></a> requires three fields:</p>
- <ol>
- <li>the name to navigate to</li>
- <li>a <code>Vec</code> containing all parameters that need to be inserted into the path</li>
- <li>optionally a query string to use.</li>
- </ol>
- <h3 id="the-special-root-index-name"><a class="header" href="#the-special-root-index-name">The special root index name</a></h3>
- <p>Whether we define any names or not, the router always knows about the
- <a href="https://docs.rs/dioxus-router-core/latest/dioxus_router_core/prelude/struct.RootIndex.html"><code>RootIndex</code></a> name. Navigating to it tells the router to go to <code>/</code>.</p>
- <p>We can change the link in our <code>NavBar</code> component to take advantage of that.</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">
- </span>fn NavBar(cx: Scope) -> Element {
- render! {
- nav {
- ul {
- li { Link { target: named::<RootIndex>(), "Home" } }
- li { Link { target: "/blog", "Blog" } }
- }
- }
- }
- }
- <span class="boring">}
- </span></code></pre></pre>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="redirection-perfection"><a class="header" href="#redirection-perfection">Redirection Perfection</a></h1>
- <p>You're well on your way to becoming a routing master!</p>
- <p>In this chapter we will cover utilizing redirects so you can take Rickrolling to
- the next level.</p>
- <h2 id="what-is-this-redirect-thing"><a class="header" href="#what-is-this-redirect-thing">What Is This Redirect Thing?</a></h2>
- <p>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.</p>
- <p>As a simple example, let's say you want user to still land on your blog, even
- if they used the path <code>/myblog</code>.</p>
- <p>All we need to do is update our route definition in our app component:</p>
- <pre><pre class="playground"><code class="language-rust no_run edition2021">
- <span class="boring">#![allow(unused)]
- </span><span class="boring">fn main() {
- </span><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">use dioxus::prelude::*;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">use dioxus_router::prelude::*;
- </span><span class="boring">fn Blog(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn BlogList(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">struct PostId;
- </span><span class="boring">struct BlogPostName;
- </span><span class="boring">fn BlogPost(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn Home(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn NavBar(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
- </span><span class="boring">fn App(cx: Scope) -> Element {
- </span>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))
- }
- );
- <span class="boring">unimplemented!()
- </span><span class="boring">}
- </span><span class="boring">}
- </span></code></pre></pre>
- <p>That's it! Now your users will be redirected to the blog.</p>
- <p>Notice that the <code>"/blog"</code> <code>str</code> is a <a href="example/./navigation-targets.html">navigation target</a>.
- We could also use external or named targets.</p>
- <h3 id="conclusion-2"><a class="header" href="#conclusion-2">Conclusion</a></h3>
- <p>Well done! You've completed the Dioxus Router guide book. 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
- can check out the <a href="https://docs.rs/dioxus-router/">API reference</a>.</p>
- <h3 id="challenges"><a class="header" href="#challenges">Challenges</a></h3>
- <ul>
- <li>Organize your components into seperate files for better maintainability.</li>
- <li>Give your app some style if you haven't already.</li>
- <li>Build an about page so your visitors know who you are.</li>
- <li>Add a user system that uses URL parameters.</li>
- <li>Create a simple admin system to create, delete, and edit blogs.</li>
- <li>If you want to go to the max, hook up your application to a rest API and database.</li>
- </ul>
- <div style="break-before: page; page-break-before: always;"></div><h1 id="full-code-3"><a class="header" href="#full-code-3">Full Code</a></h1>
- <pre><pre class="playground"><code class="language-rust no_run edition2021"><span class="boring">// Hidden lines (like this one) make the documentation tests work.
- </span><span class="boring">extern crate dioxus;
- </span><span class="boring">extern crate dioxus_router;
- </span><span class="boring">extern crate dioxus_web;
- </span>use dioxus::prelude::*;
- use dioxus_router::prelude::*;
- fn main() {
- dioxus_web::launch(App);
- }
- 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))
- }
- );
- render! {
- NavBar {}
- Outlet {}
- }
- }
- fn NavBar(cx: Scope) -> Element {
- render! {
- nav {
- ul {
- li { Link { target: named::<RootIndex>(), "Home" } }
- li { Link { target: "/blog", "Blog" } }
- }
- }
- }
- }
- fn Home(cx: Scope) -> Element {
- render! {
- h1 { "Welcome to the Dioxus Blog!" }
- }
- }
- fn Blog(cx: Scope) -> Element {
- render! {
- h1 { "Blog" }
- Outlet {}
- }
- }
- 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>("2"),
- "Read the second blog post"
- } }
- }
- }
- }
- struct PostId;
- struct BlogPostName;
- 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}"}
- }
- }
- fn PageNotFound(cx: Scope) -> Element {
- render! {
- h1 { "Page not found" }
- p { "We are terribly sorry, but the page you requested doesn't exist." }
- }
- }
- </code></pre></pre>
- </main>
- <nav class="nav-wrapper" aria-label="Page navigation">
- <!-- Mobile navigation buttons -->
- <div style="clear: both"></div>
- </nav>
- </div>
- </div>
- <nav class="nav-wide-wrapper" aria-label="Page navigation">
- </nav>
- </div>
- <script type="text/javascript">
- window.playground_copyable = true;
- </script>
- <script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
- <script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
- <script src="searcher.js" type="text/javascript" charset="utf-8"></script>
- <script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
- <script src="highlight.js" type="text/javascript" charset="utf-8"></script>
- <script src="book.js" type="text/javascript" charset="utf-8"></script>
- <!-- Custom JS scripts -->
- <script type="text/javascript">
- window.addEventListener('load', function() {
- window.setTimeout(window.print, 100);
- });
- </script>
- </body>
- </html>
|