Gy3ZRPV8SYZ53gDjSFGpi7ej1KCaPY791pMbjB9m
Bookmark

Building a Rust-powered Frontend Portfolio Website with Leptos

Building a Rust-powered Frontend Portfolio Website with Leptos

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 the rust-analyzer tool:
"rust-analyzer.procMacro.ignored": {
    "leptos_macro": [
        // optional:
        // "component",
        "server"
    ],
}

3. Setting up the Project Structure:

  • Create index.html: Create an index.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 and mod.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 the src directory to manage library components.
pub mod components;
  • Main Entry Point: Update the src/main.rs file with the following code, which mounts the App 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 generated tailwind.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 the components/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 a vercel_deploy.yml file within the workflows 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 a vercel_preview.yml file within the workflows directory. This workflow will trigger a preview deployment upon every pull request to your main 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:

Posting Komentar

Posting Komentar