Building a Rust-powered Frontend Portfolio Website with Leptos
This comprehensive guide will walk you through the process of creating a dynamic and performant frontend portfolio website using Leptos, a Rust-based framework. This approach allows you to leverage the power and safety of Rust while enjoying the flexibility of web development.
Why Rust for Frontend?
While JavaScript frameworks like React and Svelte dominate the frontend landscape, choosing Rust for frontend development presents distinct advantages:
- Performance: Rust compiles to WebAssembly (Wasm), a low-level binary format designed for high-performance web applications. This results in significantly faster code execution compared to traditional JavaScript.
- Memory Safety: Rust's robust compiler ensures memory safety, preventing common programming errors like buffer overflows and dangling pointers, leading to more stable and reliable code.
- Efficiency: Rust's strong static typing and powerful borrow checker allow you to write concise and efficient code, reducing boilerplate and potential for errors.
- Security: Rust's memory safety features also contribute to enhanced security, making your website less vulnerable to exploits.
Understanding Reactive Programming and WebAssembly
Before diving into the Leptos framework, let's grasp two fundamental concepts:
Reactive Programming:
Reactive programming, a paradigm that deals with data streams and event propagation, plays a vital role in modern frontend frameworks. It enables real-time updates and responses to user interactions. Here's how it works:
- Streams: Think of streams as pipelines through which data flows over time. Each value in the stream triggers a reaction.
- Observables: In frameworks like Angular or React, Observables represent these streams, allowing you to observe changes in data and react accordingly.
- Asynchronous Nature: Reactive programming is inherently asynchronous, meaning it allows for non-blocking operations, ensuring smooth user experiences even with complex computations.
Example:
Imagine a simple counter application with two variables: x
and y
, and a third variable z
that represents their sum.
let x = 10;
let y = 20;
let z = x + y; // z is initially 30
Now, if we change the value of y
to 40
, the traditional approach would not update z
automatically. Reactive programming, however, will trigger an update to z
in real-time, reflecting the change in y
.
WebAssembly (Wasm):
WebAssembly (Wasm) is a low-level bytecode format that can be executed in web browsers. It provides a way for languages like Rust to compile to a format that runs efficiently on the web. Key advantages of Wasm include:
- High Performance: Wasm executes at near-native speed, significantly improving web application performance.
- Platform Independence: Wasm code can run across different browsers and platforms without any modifications.
- Security: Wasm runs in a sandbox environment, preventing malicious code from accessing sensitive data.
Exploring Rust Frontend Frameworks
Several frameworks enable Rust development for the frontend, each with its unique features and approaches:
1. Leptos:
Leptos, a popular Rust-based frontend framework, provides a clean and intuitive syntax. It's highly performant, achieving a performance level close to VanillaJS, thanks to its efficient approach to memory management and the absence of a virtual DOM.
- Code Style: Leptos closely resembles SolidJS in terms of its code style, offering a familiar experience for developers coming from other JavaScript frameworks.
- Rendering: It supports both Client-side Rendering (CSR) and Server-side Rendering (SSR).
- Build System: Leptos utilizes Trunk for building Wasm files, making the process seamless and straightforward.
- Size: Leptos produces lightweight Wasm files, reducing load times and improving overall performance.
- Memory Management: Leptos leverages Rust's memory management capabilities, ensuring optimal resource utilization.
- Virtual DOM: Leptos avoids a virtual DOM, directly manipulating the real DOM for enhanced performance.
Website: https://leptos.dev/
2. Yew:
Yew is another well-established Rust frontend framework that closely resembles React in its component-based approach.
- Community: Yew boasts a thriving community, thanks to its longer existence compared to Leptos.
- Build System: Yew uses the traditional approach to building Wasm files, resulting in moderate file sizes.
- Memory Management: Yew utilizes Rust's memory management rules for efficient resource allocation.
- Virtual DOM: Yew employs a virtual DOM, which creates a virtual representation of the UI and updates it efficiently.
Website: https://yew.rs/
Choosing the Right Framework:
While both Leptos and Yew offer compelling features, Leptos stands out for its lightweight approach, high performance, and similarity to modern JavaScript frameworks like SolidJS. This guide will focus on Leptos.
Step-by-Step Tutorial: Building a Portfolio Website with Leptos
Let's embark on the journey of creating a basic portfolio website using Leptos, TailwindCSS, and Vercel for deployment:
1. Setting up the Development Environment:
- Install Rust and Node.js: Ensure that you have Rust and Node.js installed on your system.
- Install Trunk: Use Cargo to install Trunk, a powerful tool for building Wasm projects.
cargo install trunk
2. Initializing the Leptos Project:
- Create a New Project: Use Cargo to create a new Leptos project. We'll opt for a Client-side Rendering (CSR) setup.
cargo new leptos-csr-test
- Add Leptos Dependency: Add the Leptos dependency to your project. Include the "csr" feature for Client-side Rendering and the "nightly" feature to use the latest Rust features.
cargo add leptos --features=csr,nightly
- Install Nightly Rust: If you haven't already, install the nightly version of Rust:
rustup toolchain install nightly
- Override Toolchain: Set the nightly version for your project, ensuring you're working with the latest features.
rustup override set nightly
- Configure Rust Analyzer (Optional): If you're using VS Code, update your
settings.json
to prevent errors from therust-analyzer
tool:
"rust-analyzer.procMacro.ignored": {
"leptos_macro": [
// optional:
// "component",
"server"
],
}
3. Setting up the Project Structure:
- Create
index.html
: Create anindex.html
file in the root of your project.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hello Leptos</title>
</head>
<body></body>
</html>
- Install Console Error Panic Hook: Add a library to log errors to the console for debugging.
cargo add console_error_panic_hook
Create Components: Create a directory called
components
and two files inside:app.rs
andmod.rs
.Components/mod.rs: This file re-exports the
app
component for use in other files.
pub mod app;
- Components/app.rs: This file defines the main
App
component.
use leptos::*;
#[component]
pub fn App() -> impl IntoView {
view! {
<div class="font-semibold text-xl">
"Hello, Leptos!"
</div>
}
}
- Manage Library: Create a file called
lib.rs
in thesrc
directory to manage library components.
pub mod components;
- Main Entry Point: Update the
src/main.rs
file with the following code, which mounts theApp
component to the webpage.
use leptos::*;
use leptos_csr_test::components::app::App;
fn main() {
console_error_panic_hook::set_once();
mount_to_body(|| view! { <App/> })
}
4. Building and Running the Project:
- Start the Project: Use Trunk to build and start the development server.
cargo build
trunk serve --open
- Access the Website: Open your web browser and navigate to
http://127.0.0.1:8080/
to view your basic Leptos website.
Note: The default port is 8080, but you can change it if needed:
trunk serve --port 3000 --open
5. Integrating TailwindCSS for Styling:
- Install TailwindCSS: Use Node.js to install TailwindCSS.
npm install -D tailwindcss
npx tailwindcss init
- Configure
.gitignore
: Update your.gitignore
file to exclude unnecessary files for Git version control:
# ...other ignored files...
/target/
node_modules/
dist/
- Configure Tailwind Configuration: Modify the
tailwind.config.js
file to work with Rust and HTML files.
module.exports = {
purge: {
mode: "all",
content: [
"./src/**/*.rs",
"./index.html",
"./src/**/*.html",
"./src/**/*.css",
],
},
theme: {},
variants: {},
plugins: [],
};
- Create Input CSS File: Create a file named
input.css
.
@tailwind base;
@tailwind components;
@tailwind utilities;
- Generate Tailwind CSS: Use TailwindCSS to generate the final CSS file.
npx tailwindcss -i ./input.css -o ./tailwind.css --watch
Note: Remember to run this command whenever you make changes to your styling in the HTML files.
- Load TailwindCSS: Update the
index.html
file to load the generatedtailwind.css
file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link data-trunk href="./tailwind.css" rel="css" />
<title>Hello Leptos</title>
</head>
<body></body>
</html>
- Restart the Development Server: Restart the Trunk server to apply the changes.
6. Implementing Styling and Content:
- Custom Colors: Add custom colors to your
tailwind.config.js
file.
module.exports = {
// ...other configurations...
theme: {
extend: {
colors: {
"mycolor-1": "#F94892",
"mycolor-2": "#FF7F3F",
"mycolor-3": "#FBDF07",
"mycolor-4": "#89CFFD",
},
},
},
// ...other configurations...
};
- Update
App
Component: Modify thecomponents/app.rs
file to include styling and content.
use leptos::*;
#[component]
pub fn App() -> impl IntoView {
view! {
<div class="flex flex-col justify-center font-semibold space-y-1">
<div class="text-xl">
"Dancing with My "
<span class="text-mycolor-1">"C"</span>
<span class="text-mycolor-2">"o"</span>
<span class="text-mycolor-3">"d"</span>
<span class="text-mycolor-4">"e"</span>
</div>
<div class="text-base font-light">"ダンシング・ウィズ・マイ・コード"</div>
</div>
}
}
- Save and Refresh: Save your changes and refresh the browser to see the updated website.
7. Troubleshooting Build Errors:
If you encounter build errors, try adjusting the target
and rustflags
settings in your ~/.cargo/config.toml
file.
[build]
target = "wasm32-unknown-unknown"
[target.wasm32-unknown-unknown]
rustflags = ["--cfg=web_sys_unstable_apis"]
8. Deploying to Vercel using Github Actions:
- Create a Github Repository: Push your project to a new Github repository.
- Create a Vercel Account: Sign up for a free Vercel account and link it to your Github.
Github Actions:
Create a
Secrets
File: Go to your repository's settings in Github, select "Secrets and variables," and then "Actions." Create the following secrets:VERCEL_TOKEN
: Your Vercel Access Token (obtained from https://vercel.com/account/tokens)VERCEL_PROJECT_ID
: Your Vercel Project ID (found in your project's.vercel/project.json
file)VERCEL_ORG_ID
: Your Vercel Organization ID (also found in the.vercel/project.json
file)
Create
workflows
Folder: Create a.github/workflows
directory in your repository.Create
vercel_deploy.yml
: Add the following configuration to avercel_deploy.yml
file within theworkflows
directory. This workflow defines the steps to deploy your project to Vercel.
name: Release to Vercel
on:
push:
branches:
- main
env:
CARGO_TERM_COLOR: always
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
jobs:
Vercel-Production-Deployment:
runs-on: ubuntu-latest
environment: production
steps:
- name: git-checkout
uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- name: Setup Rust
run: rustup target add wasm32-unknown-unknown
- name: Download and install Trunk binary
run: wget -qO- https://github.com/trunk-rs/trunk/releases/download/v0.20.3/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf-
- name: Build with Trunk
run: ./trunk build --release
- name: Install Vercel CLI
run: npm install --global vercel@latest
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy to Vercel & Display URL
id: deployment
working-directory: ./dist
run: |
vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }} >> $GITHUB_STEP_SUMMARY
echo $GITHUB_STEP_SUMMARY
- Create
vercel_preview.yml
: Add the following configuration to avercel_preview.yml
file within theworkflows
directory. This workflow will trigger a preview deployment upon every pull request to yourmain
branch.
name: Leptos CSR Vercel Preview
on:
pull_request:
branches: [ "main" ]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
jobs:
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- name: Enforce formatting
run: cargo fmt --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- name: Linting
run: cargo clippy -- -D warnings
test:
name: Test
runs-on: ubuntu-latest
needs: [fmt, clippy]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test
build-and-preview-deploy:
runs-on: ubuntu-latest
name: Build and Preview
needs: [test, clippy, fmt]
permissions:
pull-requests: write
environment:
name: preview
url: ${{ steps.preview.outputs.preview-url }}
steps:
- name: git-checkout
uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@v2
- name: Build
run: rustup target add wasm32-unknown-unknown
- name: Download and install Trunk binary
run: wget -qO- https://github.com/trunk-rs/trunk/releases/download/v0.20.3/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf-
- name: Build with Trunk
run: ./trunk build --release
- name: Preview Deploy
id: preview
uses: amondnet/vercel-action@v25.1.1
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
github-comment: true
working-directory: ./dist
- name: Display Deployed URL
run: |
echo "Deployed app URL: ${{ steps.preview.outputs.preview-url }}" >> $GITHUB_STEP_SUMMARY
- Commit and Push: Commit your changes and push them to the
main
branch. Github Actions will automatically build and deploy your Rust frontend website to Vercel.
Conclusion:
Building a frontend portfolio website using Rust and Leptos opens up a world of possibilities for creating performant, secure, and efficient web applications. This guide has provided a comprehensive introduction to the process, covering setup, development, styling, and deployment. Remember to explore the wealth of resources available for Leptos and Rust to expand your knowledge and build even more sophisticated projects.
References:
- Leptos Documentation: https://book.leptos.dev/
- Yew Documentation: https://yew.rs/
- Reactive Programming: https://www.babelcoder.com/reactive-programming-rxjs-observables/
- TailwindCSS Integration: https://dev.to/swatinem/how-to-set-up-tailwind-css-with-yew-and-trunk-4d57
Posting Komentar