方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

[Interview AI] Changing from Bootstrap to shadcn UI: Fast and Lightweight UI

bootstrap-to-shadcn
This article can be read in about 14 minutes.

Introduction.

Currently developing a personal “Interview AI” that can transcribe an hour’s worth of audio in 15 seconds and automatically convert it into a natural conversational format.

https://www.interview-ai.site

Yesterday, I had a chance to talk with Ryusei, the founder of ReachBest, a service that helps high school students apply to overseas universities using AI. When I showed him the demo of the interview AI I was developing and told him that I was creating the UI with Bootstrap, he recommended a UI framework called Shadcn UI, so I decided to switch immediately.

https://reachbest.co

Advantages of changing to shadcn UI

shadcn/ui is a collection of highly flexible UI components developed using Radix UI and TailwindCSS.

shadcn UI allows only the necessary components to be imported and used individually, making the application lightweight. In contrast, Bootstrap usually imports the entire stylesheet, which often includes unnecessary styles. This can affect file size and loading time.

https://ui.shadcn.com

Also, shadcn UI is designed as a React component, so it is rendered as needed and DOM changes are optimized; Bootstrap’s JavaScript components have many dependencies that sometimes impact performance.

Steps to change from Bootstrap to shadcn UI in React

Uninstall Bootstrap

Remove Bootstrap from the project (this time in frontend/).

ShellScript
npm uninstall bootstrap react-bootstrap

Installation of shadcn UI

Add shadcn UI to the project (this time run in frontend/).

ShellScript
npm install @shadcn/ui

Required Dependencies

Since shadcn/ui is based on Tailwind CSS, Tailwind CSS must also be installed and configured. Execute the following command to install Tailwind CSS.

ShellScript
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Tailwind CSS Settings

Since tailwind.config.js andpostcss.config.js are generated, open tailwind.config.js and configure as follows.

In this case, we created brand, brand-dark, and brand-light as extensions of the brand color theme.

JavaScript
// tailwind.config.js
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      colors: {
        brand: "#3d5a80",
        "brand-dark": "#2a4563",
        "brand-light": "#4a7ea8",
      },
    },
  },
  plugins: [],
};

CSS file settings

Set src/index.css (or the appropriate CSS file in your project) as follows

JavaScript
@tailwind base;
@tailwind components;
@tailwind utilities;

Change code in App.tsx

Change the code in App.tsx, the entry point of the application, to implement buttons and navigation using shadcn UI.

TypeScript
// App.tsx
import { onAuthStateChanged } from "firebase/auth";
import React, { useCallback, useEffect, useState } from "react";
import { Link, Navigate, Route, Routes, useNavigate } from "react-router-dom";
import "./App.css";
import Login from "./components/Auth/Login";
import PasswordReset from "./components/Auth/PasswordReset";
import Register from "./components/Auth/Register";
import SubscriptionForm from "./components/SubscriptionForm";
import TitleAndHeaderGenerator from "./components/TitleAndHeaderGenerator/TitleAndHeaderGenerator";
import Transcription from "./components/Transcription/Transcription";
import { deleteAccount, logout } from "./services/authService";
import { auth } from "./services/firebaseConfig";

import Navbar from "./components/Navbar";
import { Button } from "./components/ui/button";

type UsageData = {
  totalMinutesUsed: number;
  maxMinutes: number;
  titlesAndHeadingsUsage: number;
  maxTitlesAndHeadingsUsage: number;
  resetDate: string | null;
  plan: string;
};

const App: React.FC = () => {
  const navigate = useNavigate();
  const [activeService, setActiveService] = useState("transcription");
  const [transcriptionData, setTranscriptionData] = useState(null);
  const [rewrittenText, setRewrittenText] = useState(null);
  const [audioFileUrl, setAudioFileUrl] = useState(null);
  const [titleHeaderData, setTitleHeaderData] = useState(null);
  const [currentUser, setCurrentUser] = useState(null);

  const [isUploading, setIsUploading] = useState(false);

  const [usageData, setUsageData] = useState({
    totalMinutesUsed: 0,
    maxMinutes: 30,
    titlesAndHeadingsUsage: 0,
    maxTitlesAndHeadingsUsage: 3,
    resetDate: null,
    plan: "free",
  });

  const fetchUsageData = useCallback(async (uid: string) => {
    const backendUrl = process.env.REACT_APP_BACKEND_URL + "/api/user-usage";
    try {
      const response = await fetch(`${backendUrl}?uid=${uid}`);
      if (!response.ok) throw new Error(`サーバーエラー: ${response.status}`);
      const data = await response.json();
      setUsageData({
        totalMinutesUsed: data.totalMinutesUsed || 0,
        maxMinutes: data.maxMinutes || 30,
        titlesAndHeadingsUsage: data.titlesAndHeadingsUsage || 0,
        maxTitlesAndHeadingsUsage: data.maxTitlesAndHeadingsUsage || 3,
        resetDate: data.resetDate || null,
        plan: data.plan || "free",
      });
    } catch (error) {
      console.error("使用回数データの取得エラー:", error);
    }
  }, []);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        setCurrentUser(user);
        fetchUsageData(user.uid);
      } else {
        setCurrentUser(null);
      }
    });
    return () => unsubscribe();
  }, [fetchUsageData]);

  const handleLogout = async () => {
    try {
      await logout();
      setCurrentUser(null);
      navigate("/login");
    } catch (error) {
      console.error("Logout failed:", error);
    }
  };

  const handleDeleteAccount = async () => {
    if (window.confirm("本当に退会しますか?")) {
      try {
        await deleteAccount();
        setCurrentUser(null);
        navigate("/login");
      } catch (error) {
        console.error("Account deletion failed:", error);
      }
    }
  };

  return (
    <div>
      
      <div>
        <div>
          <div>
            
              <Button> setActiveService("transcription")}
              >
                文字起こしAI編集
              </Button>
            

            
              <Button> setActiveService("title-header-generator")}
              >
                タイトル小見出し自動生成
              </Button>
            
          </div>
        </div>

        
          <Route path="/" element={} />
          <Route path="/transcription" element={} />
          <Route path="/title-header-generator" element={} />
          <Route path="/login" element={} />
          <Route path="/register" element={} />
          <Route path="/password-reset" element={} />
          <Route path="/upgrade-plan" element={} />
        
      </div>
    </div>
  );
};

export default App;

Here is the top screen after the change

Since the same layout and colors are used in the creation as in BootStrap, there is no change in appearance, but the response may have been a little faster.

summary

shadcn UI is the perfect UI framework for React-based applications. shadcn UI uses Radix UI and Tailwind CSS, allowing for lightweight and flexible use of components. Compared to Bootstrap, it can improve application performance by removing unnecessary styles and importing only the necessary components.

Implementing shadcn UI will optimize the DOM, improve resource efficiency, and increase overall response time. In particular, projects that require customizable designs and lightweight configurations may be able to take full advantage of this benefit.

Copied title and URL