|
|
|
CroftSoft
/
Library
/
Tutorials
Rust-Dioxus Project Setup
2023 Nov 04 Sat
David Wallace Croft
Contents
Summary
This article provides step-by-step instructions for using the Rust programming
language and the Dioxus user interface (UI) library to make a web application.
As explained in the overview, the project structure supports static prerendering
with client-side hydration.
Overview
A single page application (SPA) is a web application that downloads to the
client web browser as JavaScript or WebAssembly (Wasm) code with minimal
HTML.
Once the code starts running in the browser, it generates the HTML that it
needs for the SPA dynamically as the user navigates through the application.
The SPA code can also request data from a server and integrate into the
dynamically generated webpages client-side.
In contrast to web applications that require server-side rendering (SSR) to
integrate the HTML and data, an SPA can be served from a Content Delivery
Network (CDN). Unlike a static website served from a CDN, a search engine
crawler cannot load and read the webpages directly to find content since the SPA
is delivered as code which must be run in a client to dynamically generate the
HTML.
For this reason, some prefer SSR for search engine optimization (SEO).
For SEO, some tools let you prerender the static webpages for an SPA.
Once a static webpage and the SPA code are downloaded into the browser,
the SPA code integrates with the static webpage in a process
called client-side hydration.
This allows search engine crawlers to find HTML content directly from the CDN
while also permitting the SPA code to run in the browser.
Dioxus is a UI library for the Rust programming language which can
be deployed as an SPA by compiling the Rust source code to Wasm.
This article provides step-by-step instructions for setting up a Rust-Dioxus
project to make an SPA.
The project structure supports static prerendering with client-side hydration so
that the SPA can be served from a CDN.
Project Setup
-
Install Rust
cargo --version
-
Install the Dioxus CLI "dx"
cargo install dioxus-cli
dx --version
-
Install Node.js
-
https://nodejs.org/
-
The Node.js installation includes the CLI for Node Package Manager (npm) and npx
npm --version
npx --version
-
Run the Dioxus CLI command to create a new Dioxus project directory
dx create
-
When prompted, enter the following values:
-
project-name: my-project
-
What platform are you targeting?: web
-
Should the application use the dioxus router?: true
-
How do you want to create CSS?: Vanilla
-
Change your working directory to the new project directory
cd my-project/
-
Open the project directory in your code editor
code .
-
Make an initial git commit of the default project
-
You can use git to compare the following customizations to this initial commit
git add .
git commit -m 'initial commit'
-
Overwrite your project root directory .gitignore file with the following:
/dist
/node_modules
/target
-
Compile and serve the default project
dx serve --hot-reload
-
Test the code
-
Open your browser to
http://localhost:8080/
-
Click on the buttons to verify that the WebAssembly (Wasm) is working
-
Test the hot reload feature
-
In src/main.rs, change some of the text that is displayed to the user
-
Example: Change "High-Five" to "High-five"
-
Observe that the browser automatically updates when you save your change
-
Stop the development server by pressing Control-C in the command-line terminal
-
Overwrite your Cargo.toml file
-
The referenced "prerender" binary source code will be added in a following step
[package]
# authors = ["First Middle Last <first.middle.last@example.com>"]
# description = "Dioxus SPA with static prerendering and client-side hydration"
edition = "2021"
# homepage = "https://www.example.com/"
# keywords = ["dioxus"]
# license = "MIT"
name = "my-project"
publish = false
readme = "README.md"
# repository = "https://github.com/my-github-username/my-project"
version = "0.1.0"
[[bin]]
name = "prerender"
required-features = ["prerender"]
[dependencies]
console_error_panic_hook = "0.1.7"
dioxus = "0.4.0"
dioxus-fullstack = { version = "0.4.1", optional = true }
dioxus-router = "0.4.1"
dioxus-web = "0.4.0"
log = "0.4.20"
serde = "1.0.190"
tokio = { version = "1.33.0", features = ["full"], optional = true }
wasm-logger = "0.2.0"
[features]
hydrate = ["dioxus-fullstack/router", "dioxus-web/hydrate"]
prerender = ["dioxus-fullstack/router", "dioxus-fullstack/ssr", "tokio"]
-
Rename the static assets directory
mv public/ assets/
-
Update the CSS stylesheet files
rm input.css
rm tailwind.config.js
touch assets/stylesheet.css
-
Overwrite your Dioxus.toml file with the following:
[application]
asset_dir = "assets"
default_platform = "web"
name = "my_project"
out_dir = "dist"
[web.app]
title = "My Project"
[web.watcher]
reload_html = true
watch_path = ["src", "assets"]
[web.resource]
style = ["/stylesheet.css"]
script = []
[web.resource.dev]
script = []
style = []
-
Optionally, add a rustfmt.toml to the root of your project directory
-
This optional step will customize how your Rust source code is formatted
-
If your code editor is not already set up to use this file, use "cargo fmt"
array_width = 1
fn_params_layout = "Vertical"
hard_tabs = false
match_block_trailing_comma = true
max_width = 80
merge_derives = true
newline_style = "Unix"
remove_nested_parens = true
single_line_if_else_max_width = 0
struct_lit_width = 0
struct_variant_width = 0
tab_spaces = 2
use_field_init_shorthand = true
use_try_shorthand = true
-
Replace src/main.rs
use my_project::launch;
#[cfg(feature = "hydrate")]
fn main() {
dioxus_web::launch_with_props(
dioxus_fullstack::router::RouteWithCfg::<my_project::route::Route>,
dioxus_fullstack::prelude::get_root_props_from_document()
.expect("Failed to get root props from document"),
dioxus_web::Config::default().hydrate(true),
);
}
#[cfg(not(feature = "hydrate"))]
fn main() {
launch();
}
-
Make src/lib.rs
use components::app::App;
pub mod components;
pub mod route;
pub fn launch() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
dioxus_web::launch(App)
}
-
Make src/route.rs
use crate::components::colophon::Colophon;
use crate::components::home::Home;
use crate::components::page_not_found::PageNotFound;
use crate::components::page_template::PageTemplate;
use dioxus::prelude::*;
use dioxus_router::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, PartialEq, Routable, Serialize)]
pub enum Route {
#[layout(PageTemplate)]
#[route("/")]
Home {},
#[route("/colophon")]
Colophon {},
#[end_layout]
#[route("/:..route")]
PageNotFound {
route: Vec<String>,
},
}
-
Make the binaries subdirectory
mkdir src/bin/
-
Make src/bin/prerender.rs
-
This is the code to support static prerendering
use dioxus_fullstack::prelude::*;
use my_project::route::Route;
#[tokio::main]
async fn main() {
pre_cache_static_routes_with_props(
&ServeConfigBuilder::new_with_router(
dioxus_fullstack::router::FullstackRouterConfig::<Route>::default(),
)
.assets_path("dist")
.incremental(IncrementalRendererConfig::default().static_dir("dist"))
.build(),
)
.await
.unwrap();
}
-
Make the components subdirectory
mkdir src/components/
-
Make src/components/mod.rs
pub mod app;
pub mod colophon;
pub mod high_five;
pub mod home;
pub mod nav;
pub mod page_not_found;
pub mod page_template;
-
Make src/components/app.rs
use crate::route::Route;
use dioxus::prelude::*;
use dioxus_router::prelude::*;
#[allow(non_snake_case)]
pub fn App(cx: Scope) -> Element {
render! {
Router::<Route> { }
}
}
-
Make src/components/colophon.rs
use crate::components::high_five::HighFive;
use dioxus::prelude::*;
#[allow(non_snake_case)]
pub fn Colophon(cx: Scope) -> Element {
render! {
h1 { "Colophon Page" }
p {
"This website was created using the Rust library ",
a {
href: "https://dioxuslabs.com/",
target: "_blank",
"Dioxus",
},
"."
}
HighFive { }
}
}
-
Make src/components/high_five.rs
use dioxus::prelude::*;
#[allow(non_snake_case)]
pub fn HighFive(cx: Scope) -> Element {
let mut count = use_state(cx, || 0);
render! {
h1 {
"High-Five counter: {count}"
}
button {
onclick: move |_| count += 1,
"Up high!"
}
button {
onclick: move |_| count -= 1,
"Down low!"
}
}
}
-
Make src/components/home.rs
use crate::components::high_five::HighFive;
use dioxus::prelude::*;
#[allow(non_snake_case)]
pub fn Home(cx: Scope) -> Element {
render! {
h1 {
"Home Page"
}
p {
"This line is a placeholder for home page content."
}
HighFive { }
}
}
-
Make src/components/nav.rs
use crate::route::Route;
use dioxus::prelude::*;
use dioxus_router::prelude::*;
#[allow(non_snake_case)]
pub fn Nav(cx: Scope) -> Element {
render! {
nav {
ul {
li {
Link {
to: Route::Home {},
"Home"
}
}
li {
Link {
to: Route::Colophon {},
"Colophon"
}
}
}
}
}
}
-
Make src/components/page_not_found.rs
use dioxus::prelude::*;
#[allow(non_snake_case)]
#[inline_props]
pub fn PageNotFound(
cx: Scope,
route: Vec<String>,
) -> Element {
render! {
h1 {
"Page Not Found"
}
pre {
color: "red",
"{route:?}"
}
}
}
-
Make src/components/page_template.rs
use crate::components::nav::Nav;
use crate::route::Route;
use dioxus::prelude::*;
use dioxus_router::prelude::*;
#[allow(non_snake_case)]
pub fn PageTemplate(cx: Scope) -> Element {
render! {
Nav { }
Outlet::<Route> {}
}
}
-
Compile and serve the customized project
dx serve --hot-reload
-
Test the code
-
Open your browser to
http://localhost:8080/
-
Click on the buttons on the Home page to verify that the Wasm is working
-
Click on the "Colophon" link in the navigation section
-
Click on the buttons on the Colophon page to verify that the Wasm is working
-
View the page source for the Home and Colophon pages
-
Right-click on a webpage and select "View Page Source"
-
Note how the page content is not visible within the HTML
-
This will be fixed in a later step
-
Attempt to open the Colophon page directly in your browser
-
Stop the development server by pressing Control-C in the command-line terminal
-
Build the Single Page Application (SPA) with client-side hydration
-
This will recreate your distribution directory dist/
dx build --features=hydrate --release
-
Insert the prerendered static HyperText Markup Language (HTML)
-
This will update the HTML in dist/
cargo run --bin prerender --features=prerender --release
-
Serve the HTML and Wasm from your distribution directory
-
The following command will automatically open your browser
npx http-server dist -o
-
Verify that the webpage includes the text content
-
Press the reload button on your browser while holding the Shift key
-
Right-click on a webpage and select "View Page Source"
-
Note how the text content is visible within the HTML for search engines to find
-
Open the Colophon page directly in your browser
-
Enter the URL
http://localhost:8080/colophon/ in your browser toolbar
-
Note that the page loads without having to go to the Home page first
-
Click the buttons to verify that the Wasm is working
NPM Run Scripts
This optional section describes how to add Node Package Manager (npm) run
scripts to make development and testing of your Dioxus project easier.
-
Make a package.json file in your project root directory
{
"devDependencies": {
"http-server": "^14.1.1",
"prettier": "3.0.3",
"rimraf": "^5.0.1"
},
"scripts": {
"clean": "rimraf dist",
"dist": "npm run clean && npm run make",
"format": "prettier dist --ignore-path .prettierignore --write",
"hydrate": "dx build --features=hydrate --release",
"prerender": "cargo run --bin prerender --features=prerender --release",
"make": "npm run hydrate && npm run prerender && npm run format",
"serve": "http-server dist -o",
"start": "dx serve --hot-reload",
"test": "npm run dist && npm run serve"
}
}
-
Install the dependencies
npm install
-
Test your npm run scripts
npm test
-
Update the README.md in your project root directory
# My Project
- A description of My Project
- Makes a Content Delivery Network (CDN)-compatible static HTML distribution
- Includes static prerendering with client-side hydration
## Utilities Installation
- Install the Rust command line utility "cargo"
- cargo is installed when you install Rust
- https://www.rust-lang.org/
- Install the Dioxus Command Line Interface (CLI) "dx"
- cargo install dioxus-cli
- https://github.com/DioxusLabs/dioxus/tree/master/packages/cli
- Install npm
- npm installs utilities such as prettier
- npm scripts run the dx and cargo commands
- npm can be installed by installing node.js
- https://nodejs.org/
## Hot Reload
- cd my-project/
- npm install
- Installs the utility http-server to serve the HTML
- Installs the utility pretter to format the HTML
- Installs the utility rimraf to remove distribution directory dist/
- npm start
- Open your browser to http://localhost:8080/
- Make changes to the HTML in src/lib.rs or the CSS in public/stylesheet.css
- Note that the changes are updated in your browser as soon as you save
## Test Static Prerendering with Hydration
- npm test
- Deletes the distribution directory dist/ to start clean
- Makes the index.html page with the hydration code
- Inserts the prerendered HTML
- Formats the HTML using the prettier utility
- Launches http-server to serve the HTML
- Opens your browser to the home page
## Other Commands
- npm run clean
- Deletes the distribution directory dist/ to start clean
- npm run dist
- Same as npm test
- Except that it does not start http-server and open the browser
- npm run format
- Runs the utility prettier
- npm run hydrate
- Makes the index.html page with the hydration code
- npm run prerender
- Inserts the prerendered HTML
- npm run make
- Makes the index.html page with the hydration code
- Inserts the prerendered HTML
- Runs the utility prettier
- But does not start by deleting dist/
- npm run serve
- Starts the http-server
- Opens the browser
## Links
- CroftSoft Rust-Dioxus Project Setup Tutorial
- https://www.croftsoft.com/library/tutorials/rust-dioxus-project-setup/
## History
- Initial release: YYYY-MM-DD
Links
In recommended reading order:
© 2023
CroftSoft Inc
|
|
|
|
|
|