跪拜 Guibai
← Back to the summary

Building a Reusable WebGPU Model-Loading UI with React and TailwindCSS

Hello everyone, in the previous chapter "Abandoning Cloud APIs, Running the DeepSeek Large Model Locally in the Browser with WebGPU", we completed the project architecture setup, WebGPU environment detection, and basic page layout, thoroughly understanding the core advantages of on-device AI compared to cloud APIs.

In this chapter, we enter advanced engineering practice. AI projects differ from typical backend management systems, involving complex scenarios such as large file model loading, progress monitoring, state switching, and asynchronous interactions.

Therefore, this chapter focuses on solving: How to build a maintainable and reusable on-device AI project with a React engineering mindset. The core implementations are: component splitting philosophy, React synthetic event principles, encapsulating a universal model progress bar component, and state-driven model loading views, laying a solid foundation for actually loading ONNX models in the next chapter.


1. Why Must AI Frontend Projects Use React? (Core Highlights for Interviews/Resumes)

Many beginners are puzzled: Vue is quicker to pick up and has simpler syntax, so why do mainstream AI projects, large model interaction systems, and AI training visualization projects uniformly use React + TS?

1. Project Scale Adaptability

Vue is rich in syntactic sugar and has a low barrier to entry, making it suitable for small to medium-sized rapid iteration projects; whereas React has stricter syntax, purer functional programming ideas, and stronger engineering constraints, making it the first choice for large, complex projects.

On-device AI projects involve model loading, asynchronous inference, progress monitoring, multi-level state switching, and nested component rendering. The business logic is far more complex than a typical page, fully highlighting React's architectural advantages.

2. Absolute Monopoly in the AI Ecosystem

Currently, the vast majority of global AI frontend training code, inference demos, WebGPU compute adaptation, and Transformers.js examples are all developed based on the React ecosystem, with very few related resources in the Vue ecosystem. For AI frontend work, React is a mandatory tech stack.

3. Engineering Standards: Strong Constraints with ESLint + TS

This project adopts the enterprise-standard combination of React + TS + ESLint:


2. Core Technical Principles Explained (High-Frequency Interview Topics)

1. The Underlying Principles of JSX and className

JSX is a core feature of React, short for JavaScript + XML. It allows writing HTML tags directly in JS code, which are compiled into native DOM operations, greatly improving UI development efficiency.

The most common pitfall for beginners: You cannot use class to define styles in JSX.

This is because class is a reserved keyword in JS object-oriented programming, used for declaring classes. Therefore, React specifically uses className to replace the native HTML class attribute, perfectly avoiding syntax conflicts.

2. The Revolutionary Advantages of Atomic CSS with TailwindCSS

Traditional development involves handwriting CSS selectors and style rules, which is low-level, inefficient programming with lots of repetitive code and high maintenance costs.

TailwindCSS is the standard UI framework for modern AI projects and Vibe Coding. Its core principles:

All layouts, centering, color schemes, and hover interactions in this project are implemented through Tailwind atomic classes, truly achieving zero handwritten CSS.

3. React Synthetic Events (Essential for Senior Frontend Engineers)

Many people only know how to write onClick but don't understand the underlying principles. This is a key differentiator between junior and mid-level frontend engineers.

Native DOM Event Evolution:

React follows the principle of least innovation, not inventing new syntax but directly reusing native event concepts:

The onClick in React is not a native DOM event, but a Synthetic Event. Under the hood, it performs event delegation, browser compatibility encapsulation, and performance optimization, unifying the event execution mechanisms across different browsers.

This is also a reflection of React's code purity and engineering elegance.


3. Core of Frontend Engineering: Component Splitting and the Component Tree Mindset

1. The Essential Difference Between Vue and React Components

Vue Components: A sandwich structure, one file contains template, script, and style. Easy to start, fixed structure.

React Components: A function is a component. A function that returns JSX is an independent component.

JS logic is written inside the function, the return value renders the UI, and styles are introduced via Tailwind or externally. Logic and view are highly integrated, making it more suitable for encapsulating complex AI business logic.

2. The Core Value of Componentization (Project Highlight)

The evolution of frontend projects from a DOM tree to a component tree is an inevitable trend in the industry:


4. Core Practice: Encapsulating a Reusable Model Progress Bar Component

In on-device AI scenarios, model (.onnx) files are huge and take a long time to load. Progress bar display is a core interactive experience. We extract the progress module from the page and encapsulate it as a universal business component.

1. Creating the Progress.tsx Component

Receives dynamic parameters via props to achieve a universal display for any file, any progress, and any size, completely decoupling it from the business page.

// Pure display reusable component, no internal state, driven by parent component data
type ProgressProps = {
  text: string;
  percentage: number;
  total: number;
}

// Function = Component, props receive data passed from the parent component
const Progress = ({ text, percentage, total }: ProgressProps) => {
  return (
    <div className="w-full bg-gray-100 rounded-md p-3 my-2">
      <p className="text-sm text-gray-700 mb-1">File: {text}</p>
      <p className="text-xs text-gray-500">
        Loaded: {percentage} / Total Size: {total}
      </p>
    </div>
  )
}

export default Progress

2. Parent Component Import + Array Loop Rendering

React does not invent new syntax; it directly uses native Array.map to implement list rendering. Compared to Vue's v-for, it is more aligned with native JS syntax, with a lower learning curve and higher flexibility.


5. Integrating the Complete Business Logic (State-Driven Model Loading)

The core business logic of this chapter: Reactive data drives the view, completely abandoning native DOM operations. Model state, error state, and loading progress are managed via useState. Clicking a button triggers a state change, automatically updating the page UI.

import { useState, useEffect } from 'react';
// Import custom reusable progress bar component
import Progress from './components/Progress';

function App() {
  // 1. Core reactive state management
  // Model status: null initial / loading / ready
  const [status, setStatus] = useState<string | null>(null);
  // Error status: captures model loading exceptions
  const [error, setError] = useState<string | null>(null);
  // Loading prompt text
  const [loadingMessage, setLoadingMessage] = useState("Start loading ONNX model");
  // Multi-file progress list (real AI scenarios load multiple weight files)
  const [progressItems, setProgressItems] = useState([
    {
      text: 'model.onnx',
      percentage: 0,
      total: 34353543453
    },
    {
      text: 'tokenizer.onnx',
      percentage: 0,
      total: 14353543453
    }
  ]);

  // 2. WebGPU environment capability detection (core prerequisite)
  // Double !! converts undefined / null to a standard boolean
  const IS_WEBGPU_AVALABLE = !!navigator.gpu;

  // 3. Component lifecycle: execute side effects after mounting
  useEffect(() => {
    console.log('AI main component mounted, waiting for user to trigger model loading');
  }, []);

  return (
    IS_WEBGPU_AVALABLE ? (
      <div className="flex flex-col h-screen mx-auto items-center justify-end text-gray-800 bg-white">
        <div className="h-full overflow-auto flex justify-center items-center flex-col relative">
          {/* Project title introduction */}
          <div className="flex flex-col items-center mb-1 max-w-[400px] text-center">
            <h1 className="text-4xl font-bold mb-1">DeepSeek-R1 WebGPU</h1>
            <h2 className="font-semibold text-gray-600">
              Browser-side offline inference for large models
            </h2>
          </div>

          {/* Project description & tech stack explanation */}
          <div className="flex flex-col items-center px-4">
            <p className="max-w-[510px] mb-4 text-sm text-gray-600">
              Loading a lightweight distilled model
              <a
                href="https://huggingface.co/onnx-community/DeepSeek-R1-Distill-Qwen-1.5B-ONNX"
                target="_blank"
                rel="noreferrer"
                className="font-medium text-blue-500 underline mx-1"
              >
                DeepSeek-R1-Distill-Qwen-1.5B
              </a>
              , based on Transformers.js + ONNX Runtime Web for pure browser local inference. Data is not uploaded, supports offline use.
            </p>

            {/* Error state fallback display */}
            {error && (
              <div className="text-red-500 text-center mb-2 text-sm">
                <p className="mb-1">Model loading failed:</p>
                <p>{error}</p>
              </div>
            )}

            {/* Synthetic event click to load model */}
            <button
              className="border px-4 py-2 rounded-lg bg-blue-400
              text-white hover:bg-blue-500 disabled:cursor-not-allowed
              select-none transition-all"
              // State lock: prevent repeated clicks while loading/error
              disabled={status !== null || error !== null}
              onClick={() => {
                setStatus("loading");
                setLoadingMessage("Model file loading, please wait...");
              }}
            >
              Load Model
            </button>
          </div>
        </div>

        {/* Show progress bar component while loading */}
        {status === "loading" && (
          <div className="w-full max-w-[500px] text-left mx-auto p-4 mt-auto">
            <p className="text-center mb-2 text-gray-700">{loadingMessage}</p>
            {/* Native map renders multiple progress bar components */}
            {progressItems.map((item, index) => (
              <Progress
                key={index}
                text={item.text}
                percentage={item.percentage}
                total={item.total}
              />
            ))}
          </div>
        )}
      </div>
    ) : (
      // Fallback for browsers not supporting WebGPU
      <div className="w-full h-screen flex items-center justify-center text-xl text-red-500">
        Your current browser does not support WebGPU. Please upgrade to the latest Chrome / Edge browser.
      </div>
    )
  )
}

export default App

6. Summary of Core Chapter Highlights (Directly Reusable for Resumes)

All knowledge points in this chapter can be directly written into resume project descriptions, belonging to high-level frontend + AI crossover capabilities:

  1. Engineering Capability: Built an on-device AI project architecture based on React+TS+ESLint+TailwindCSS, following enterprise-level development standards.
  2. Componentization Mindset: Split a reusable progress bar business component, developed based on a component tree, achieving decoupling of view and logic, improving project maintainability.
  3. Mastery of Underlying Principles: Thoroughly understood JSX syntax, className adaptation principles, Tailwind atomic CSS operating mechanism, and the underlying logic of React synthetic events.
  4. AI Business Adaptation: Implemented complete logic for state locking, progress monitoring, exception fallback, and browser environment detection for large model loading scenarios.
  5. Reactive Development: Achieved data-driven views based on React Hooks, zero DOM operations, adapting to the complex asynchronous loading scenarios of AI.

7. Next Chapter Preview

In this chapter, we completed the model loading UI base, component encapsulation, state management, and event interaction. The next chapter will enter the core hardcore functionality:

Integrating Transformers.js to actually pull open-source ONNX models from HuggingFace, achieving real browser downloads, real-time progress monitoring, and local model inference, truly running the complete on-device AI workflow!

Continuously updating the on-device AI practical series, like and bookmark so you don't get lost!