|
|
|
CroftSoft
/
Library
/
Tutorials
Rust-Dioxus Project Setup
2024 Jan 21 Sun
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
-
Make a Rust project
-
Start by following the instructions in the
Rust Project Setup
tutorial
-
Then return to this webpage and proceed to the next step
-
Install the Dioxus Command-Line Interface (CLI) "dx"
cargo install dioxus-cli
dx --version
-
Change your working directory to the new project directory
cd project-name/
-
Open the project directory in your code editor
code .
-
Use the Dioxus CLI to create the Dioxus configuration file Dioxus.toml
dx config init project-name
-
Use the cargo CLI to add the Dioxus dependencies to your Cargo.toml file
cargo add dioxus
cargo add dioxus-web
-
Replace src/main.rs
use ::dioxus::prelude::*;
fn main() {
::dioxus_web::launch(App);
}
#[allow(non_snake_case)]
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
"Hello, world!"
}
})
}
-
Overwrite your project root directory .gitignore file with the following:
/dist
/target
-
Compile and serve the default project
dx serve --hot-reload
-
Test the code
-
Test the hot reload feature
-
In src/main.rs, change "world" to "World"
-
Observe that the browser automatically updates when you save your change
-
Stop the development server by pressing Control-C in the command-line terminal
-
Make a git commit of the Rust-Dioxus project in its current state
-
Later you can use git to compare the following customizations to this commit
git add .
git commit -m 'Dioxus hello world'
-
Push your commit to your remote repository
git push
-
Insert or overwrite the following sections of your Cargo.toml file
-
Do not overwrite the package section of your Cargo.toml file
-
The referenced "prerender" binary source code will be added in a following step
[[bin]]
name = "prerender"
required-features = ["prerender"]
[dependencies]
console_error_panic_hook = "0.1.7"
dioxus = "=0.4.3"
dioxus-fullstack = { version = "=0.4.3", optional = true }
dioxus-router = "=0.4.3"
dioxus-web = "=0.4.3"
log = "0.4.20"
serde = "1.0.195"
tokio = { version = "1.35.1", 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"]
-
Make a child directory for your static assets such as stylesheets and images
mkdir assets/
-
Make an empty CSS stylesheet file in the assets directory
touch assets/stylesheet.css
-
Overwrite your Dioxus configuration file Dioxus.toml with the following:
-
Update the placeholder value for the "name" property in the application section
-
Replace the "title" property value with what you want to go on the browser tab
[application]
asset_dir = "assets"
default_platform = "web"
name = "project-name"
out_dir = "dist"
[web.app]
title = "Project Name"
[web.watcher]
reload_html = true
watch_path = ["src", "assets"]
[web.resource]
style = ["/stylesheet.css"]
script = []
[web.resource.dev]
script = []
style = []
-
Replace the code for src/main.rs again
-
Replace the placeholder value "project_name" in two places in the following
-
Use an underscore instead of a hyphen when you replace "project_name"
#[cfg(feature = "hydrate")]
use ::dioxus_fullstack::prelude::*;
#[cfg(feature = "hydrate")]
use ::dioxus_fullstack::router::{FullstackRouterConfig, RouteWithCfg};
#[cfg(feature = "hydrate")]
use ::dioxus_web::Config;
#[cfg(feature = "hydrate")]
use project_name::route::Route;
#[cfg(feature = "hydrate")]
fn main() {
let root_properties: FullstackRouterConfig<Route> =
get_root_props_from_document()
.expect("Failed to get root properties from document");
let config = Config::default().hydrate(true);
::dioxus_web::launch_with_props(
RouteWithCfg::<Route>,
root_properties,
config,
);
}
#[cfg(not(feature = "hydrate"))]
fn main() {
project_name::launch();
}
-
Make src/lib.rs
use self::components::app::App;
use ::log::Level;
use ::wasm_logger::Config;
pub mod components;
pub mod route;
pub fn launch() {
let config = Config::new(Level::Debug);
::wasm_logger::init(config);
::dioxus_web::launch(App)
}
-
Make src/route.rs
use super::components::colophon::Colophon;
use super::components::home::Home;
use super::components::page_not_found::PageNotFound;
use super::components::template::Template;
use ::dioxus::prelude::*;
use ::dioxus_router::prelude::*;
use ::serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, PartialEq, Routable, Serialize)]
pub enum Route {
#[layout(Template)]
#[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
-
Replace the placeholder value "project_name"
-
Use an underscore instead of a hyphen when you replace "project_name"
use ::dioxus_fullstack::prelude::*;
use ::dioxus_fullstack::router::FullstackRouterConfig;
use project_name::route::Route;
const DIST: &str = "dist";
#[tokio::main]
async fn main() {
let fullstack_router_config = FullstackRouterConfig::<Route>::default();
let incremental_renderer_config =
IncrementalRendererConfig::default().static_dir(DIST);
let serve_config: ServeConfig<FullstackRouterConfig<Route>> =
ServeConfigBuilder::new_with_router(fullstack_router_config)
.assets_path(DIST)
.incremental(incremental_renderer_config)
.build();
pre_cache_static_routes_with_props(&serve_config)
.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 template;
-
Make src/components/app.rs
use super::super::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 super::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 super::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 super::super::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)]
#[component]
pub fn PageNotFound(
cx: Scope,
route: Vec<String>,
) -> Element {
render! {
h1 {
"Page Not Found"
}
pre {
color: "red",
"{route:?}"
}
}
}
-
Make src/components/template.rs
use super::super::route::Route;
use super::nav::Nav;
use ::dioxus::prelude::*;
use ::dioxus_router::prelude::*;
#[allow(non_snake_case)]
pub fn Template(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
-
Click on the "Home" link in the navigation section
-
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
-
Install Node.js
-
See the installation instructions at
https://nodejs.org/
-
The Node.js installation includes the Node Package Manager (npm) and npx CLIs
-
The npm CLI is used in the next major section which is optional
-
The npx CLI is used in the following step to execute a development HTTP server
npm --version
npx --version
-
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
-
Stop the development server by pressing Control-C in the command-line terminal
-
Make a git commit of the Rust-Dioxus project in its current state
git add .
git commit -m 'Rust-Dioxus project setup'
-
Push your commit to your remote repository
git push
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.
-
Insert the following line into the project root directory .gitignore file
-
Keep the pre-existing lines in the .gitignore file
/node_modules
-
Make a package.json file in your project root directory
{
"devDependencies": {
"http-server": "^14.1.1",
"prettier": "3.2.4",
"rimraf": "^5.0.5"
},
"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
-
Replace the placeholder values such as "Project Name" and "project-name"
# Project Name
- A description of Project Name
- 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 project-name/
- 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
-
Stop the development server by pressing Control-C in the command-line terminal
-
Make a git commit of the Rust-Dioxus project in its current state
git add .
git commit -m 'npm run scripts'
-
Push your commit to your remote repository
git push
Links
In recommended reading order:
© 2023 - 2024
CroftSoft Inc
|
|
|
|
|
|