From Tailwind CLI to Vite: A Developer’s Journey to Better Performance (2025 Edition)
gerry leo nugroho

gerry leo nugroho @gerryleonugroho

About: Gemika's Father • Data Story Telling Nerd • Coding Dad • OSX Geek • Tech Junkie • Digital Advertising Addict • Digital Marketer • Growth Hacker • Pythonista • Entrepreneur in Between

Location:
Jakarta, Indonesia
Joined:
Dec 4, 2022

From Tailwind CLI to Vite: A Developer’s Journey to Better Performance (2025 Edition)

Publish Date: Apr 27
1 0

I’m always on the lookout for tools that can make my workflow faster, more efficient, and enjoyable. Recently, I decided to integrate Vite into my existing TailwindCSS CLI project, all-things-digital. My goal was to modernize my build process while keeping the simplicity of my current setup intact. In this blog post, I’ll walk you through the entire process—from creating a backup branch, integrating Vite into my current codebase and modularizing my code to take advantage of what Vite has to offer. By the end of this journey, you’ll have a clear understanding of how to upgrade your own project with Vite while maintaining modularity and performance. And for the impatience folks, here's the Github repo URL.

GitHub logo leonism / all-things-digital

Experience the innovation of a mobile-first theme, harnessing the power of Vue, Vite & Tailwind's cutting-edge technology (upgraded to 4.1.4) and complemented by Typography and various other indispensable plugins.

👋 All Things Digital - A Tailwind Theme

Table of Contents

✅ Build Status

Deploy to GitHub Pages Netlify Status Deploy to Cloudflare Pages

🎥 Video

DGPond.COM.mp4

🎊 Descriptions

Introducing our newest CSS theme: All Things Digital, a simple, minimalistic and responsive multiple-page layout, built on top of the latest Tailwind (4.1.4) technology. All Things Digital shipped with Dark Mode and Mobile First Focus! This theme is designed to provide a sleek and modern look for your website or blogs, with a focus on accessibility and easy customization.

The dark mode feature allows users to switch to a darker color scheme for a more comfortable viewing experience, particularly at night or in low light environments. And with a mobile first focus, this theme is optimized for mobile devices, ensuring that your website looks great on any screen size.

Tailwind CSS is a utility-first…

1. Create a New Branch for Backup

Before diving into any major changes, it’s essential to create a backup branch. This ensures that your current setup remains untouched in case something goes wrong during the upgrade process. Here’s how I did it:

Step 1.1: Open Your Terminal

I opened my terminal and navigated to the root directory of my project. If you’re using a Mac or Linux, you’ll use Bash, while Windows users can use the command prompt .

Step 1.2: Create the Backup Branch

To create a new branch named latest-vite-integration, I ran the following command:

git checkout -b latest-vite-integration
Enter fullscreen mode Exit fullscreen mode

This command creates a new branch and switches to it in one step . It’s a quick and efficient way to ensure I have a safe starting point.

Step 1.3: Push the Branch to GitHub

After creating the branch locally, I pushed it to GitHub to back it up remotely:

git push origin latest-vite-integration
Enter fullscreen mode Exit fullscreen mode

This step ensures that the branch is stored securely in the cloud, making it accessible from anywhere .

Why This Step Matters

Creating a backup branch is a best practice I’ve learned over time. It gives me peace of mind knowing that I can always revert to my original setup if needed. Plus, it allows me to experiment freely without worrying about breaking anything .


2. Vite Best Practices in 2025

Before integrating Vite into my project, I wanted to ensure I understood its best practices and how it could improve my workflow. This step was crucial for making informed decisions and avoiding common pitfalls. Here’s what I learned:

What is Vite?

Vite is a modern build tool that leverages native ES modules (ESM) to provide a lightning-fast development experience . Unlike traditional bundlers like Webpack, Vite serves files on demand during development, eliminating the need for a full rebuild every time you make a change. This makes it particularly well-suited for static HTML/CSS/JS projects like mine.

Key Features of Vite

  1. Hot Module Replacement (HMR):

    One of Vite’s standout features is its HMR capability. When you edit a file, only the changed module is updated in the browser, without reloading the entire page. This makes development faster and more enjoyable .

  2. Optimized Production Builds:

    While Vite uses native ESM during development, it bundles your code into optimized assets for production. This ensures your site remains performant when deployed .

  3. Seamless Integration with Modern Tools:

    Vite works well with frameworks like React and libraries like TailwindCSS. It also supports TypeScript out of the box, making it future-proof for potential upgrades .

  4. Minimal Configuration:

    Vite is designed to require minimal setup. For most projects, the default configuration is sufficient, but it’s also highly customizable if needed .

How Vite Differs from My Current Setup

Currently, I’m using the Tailwind CLI with NPM scripts to process my CSS. While this setup works, it lacks the speed and modularity that Vite offers. Here’s a comparison:

Feature Current Setup (Tailwind CLI + NPM Scripts) Vite
Development Server No built-in server Built-in, fast, and feature-rich
Hot Module Replacement (HMR) Manual refresh required Instant updates without full reload
Production Optimization Manual minification Automatic optimization and bundling
Modularity Limited Encourages modular components via ES modules

Folder Structure Recommendations

Vite encourages a modular folder structure to keep your project organized. Based on my research, here’s how I plan to restructure my project:

src/
├── components/
│   ├── header.html
│   ├── footer.html
│   └── content.html
├── layout/
│   └── base.html
├── main.css
└── index.html
Enter fullscreen mode Exit fullscreen mode

This structure allows me to break my code into smaller, reusable components. For example, instead of writing all my HTML in a single file, I can split it into logical pieces like header, footer, and content.

Resources I Found Helpful

  • The Vite documentation provided clear guidance on setting up and configuring the tool .
  • Articles like “Why Vite is the best?” highlighted its advanced features, such as seamless form handling and built-in state management .
  • Comparisons between Vite and traditional build tools like Webpack helped me understand its advantages in terms of speed and ease of use .

Why This Step Matters

Researching Vite’s best practices gave me confidence in my decision to adopt it. By understanding its strengths and how it differs from my current setup, I was able to plan a smooth transition. Plus, learning about its modular architecture inspired me to rethink how I organize my code.


3. Install Vite and Set Up the Development Environment

With a solid understanding of Vite’s best practices, I was ready to install it and configure my development environment. This step involved setting up Vite, configuring it to work with my existing TailwindCSS setup, and ensuring everything worked seamlessly. Here’s how I did it:

Step 3.1: Install Vite & Autoprefixer

To integrate Vite into my project, I started by installing it as a development dependency. In my terminal, I ran the following command:

npm install vite --save-dev
Enter fullscreen mode Exit fullscreen mode

This command added Vite to my devDependencies in package.json, making it available for use during development .

Though my current project setup with comes with a Tailwind CSS CLI already included, but there is one more dependencies that Vite may need additionally, the autoprefixer library.

npm install -D autoprefixer --save-dev
Enter fullscreen mode Exit fullscreen mode

By invoking the previous command in your terminal, it would add "autoprefixer": "^10.4.21", to my package.json at the same time.

Step 3.2: Add a Vite Configuration File

Next, I created a vite.config.mjs file in the root directory of my project. This file allows me to customize Vite’s behavior while keeping things simple. Here’s the configuration I used:

// Import required modules for Vite configuration
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/postcss';
import autoprefixer from 'autoprefixer';

/**
 * Vite configuration for the project.
 *
 * This config sets up:
 * - Project root directory
 * - Build output settings
 * - PostCSS plugins (Tailwind CSS and Autoprefixer)
 */
export default defineConfig({
  // Set the project root directory to './src'
  root: './src',

  // Build configuration
  build: {
    // Output directory for built files (relative to project root)
    outDir: '../dist',
    // Clear the output directory before each build
    emptyOutDir: true,
  },

  // CSS processing configuration
  css: {
    // PostCSS configuration
    postcss: {
      plugins: [
        // Add Tailwind CSS as a PostCSS plugin
        tailwindcss(),
        // Add Autoprefixer for vendor prefixing
        autoprefixer()
      ]
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

Why This Configuration?

  • The root option specifies that Vite should look for my source files in the src folder.
  • The build.outDir option ensures that production builds are placed in the dist folder, matching my current workflow.
  • The css.postcss option integrates my existing PostCSS setup, ensuring TailwindCSS continues to work without issues .
"scripts": {
        "dev": "vite", // Start the Vite development server
        "build": "vite build", // Build for production
        "preview": "vite preview", // Preview the production build locally
        "watch": "tailwindcss -i ./src/main.css -o ./dist/style.css --watch",
        "compress": "npx tailwindcss -i ./src/main.css -o ./dist/style.css --minify",
        "start": "live-server ./dist"
    }
Enter fullscreen mode Exit fullscreen mode

Step 3.3: Update NPM Scripts

To make Vite part of my workflow, I updated the scripts section in my package.json. Here’s what I changed:

What Do These Scripts Do?

  • npm run dev: Starts the Vite development server, enabling hot module replacement (HMR) for a faster development experience.
  • npm run build: Creates an optimized production build in the dist folder.
  • npm run preview: Lets me preview the production build locally before deploying it.
  • npm run watch: Keeps my existing TailwindCSS workflow intact by watching for changes and rebuilding styles as needed .

4. Update Folder Structure for Modularity Using vite-plugin-ejs

To achieve modularity in my project, I decided to use the vite-plugin-ejs plugin . This plugin allows me to break my HTML into smaller, reusable components and assemble them dynamically using EJS templates. Below, I’ll walk you through the entire process—from installing the plugin to restructuring the folder and file structure, and finally modularizing the code.

Step 4.1: Install vite-plugin-ejs

The first step was to install the vite-plugin-ejs plugin as a development dependency. In my terminal, I ran the following command:

npm install vite-plugin-ejs --save-dev
Enter fullscreen mode Exit fullscreen mode

This added the plugin to my devDependencies in package.json, making it available for use in my Vite configuration .

Step 4.2: Update vite.config.js

Next, I updated my vite.config.js file to include the vite-plugin-ejs plugin. Here’s the updated configuration:

// Import required modules for Vite configuration
import { defineConfig } from 'vite'
import tailwindcss from 'tailwindcss'
import autoprefixer from 'autoprefixer'
import imagemin from 'vite-plugin-imagemin'
import htmlMinifier from 'vite-plugin-html-minifier'
import { ViteEjsPlugin } from 'vite-plugin-ejs'
import { resolve } from 'path'

/**
 * Vite configuration for the project.
 *
 * This config sets up:
 * - Project root directory
 * - Build output settings
 * - PostCSS plugins (Tailwind CSS and Autoprefixer)
 * - Images, HTML, CSS, and JavaScript compressions using Vite plugins
 * - EJS templating for HTML modularity
 */
export default defineConfig({
  // Set the project root directory to './src'
  root: './src',

  // Build configuration
  build: {
    // Output directory for built files (relative to project root)
    outDir: '../dist',
    // Clear the output directory before each build
    emptyOutDir: true,
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'src/index.html'),
        about: resolve(__dirname, 'src/about.html'),
        contact: resolve(__dirname, 'src/contact.html'),
        blog: resolve(__dirname, 'src/blog.html'),
        category: resolve(__dirname, 'src/category.html'),
      },
    },
  },

  // CSS processing configuration
  css: {
    // PostCSS configuration
    postcss: {
      plugins: [tailwindcss, autoprefixer],
    },
  },

  // Plugins configuration
  plugins: [
    // Image minification
    imagemin({
      pngquant: {
        quality: [0.7, 0.9],
        speed: 4,
      },
    }),
    // HTML minification
    htmlMinifier({
      minify: true,
      collapseWhitespace: true,
      keepClosingSlash: true,
      removeComments: true,
      removeRedundantAttributes: true,
      removeScriptTypeAttributes: true,
      removeStyleLinkTypeAttributes: true,
      useShortDoctype: true,
    }),
    // EJS templating for HTML
    ViteEjsPlugin(),
  ],

  server: {
    watch: {
      usePolling: true,
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

Why Use vite-plugin-ejs?

This plugin enables me to use EJS templates in my HTML files, allowing me to modularize my code into reusable components like header.html, navigation.html, and more .

Step 4.3: Restructure the Folder and File Structure

To modularize my code, I reorganized my project folder structure. Here’s the updated structure:

src/
├── components/
│   ├── Footer.html
│   ├── Header.html
│   ├── Navigation.html
│   ├── nav-desktop.html
│   └── nav-mobile.html
├── layouts/
│   └── base.html
├── content/
│   └── content-about.html
│   └── content-blog-grid.html
│   └── content-blog.html
│   └── content-category.html
│   └── content-home.html
├── main.css
└── index.html
Enter fullscreen mode Exit fullscreen mode

What Changed?

  • I moved all reusable components (e.g., header, footer) into the components folder.
  • I created a layouts folder to house the base.html layout file, which serves as the template for assembling components.
  • I renamed index.html to index.html to leverage EJS templating .

Step 4.4: Modularize the Code

Base Layout (base.html)

The base.html file acts as the skeleton of my HTML structure. It includes placeholders for injecting components like header, navigation, content, and footer. Here’s the full code:

<!DOCTYPE html>
<html lang="en">
  <%- include('components/Header.html') %>
  <body class="antialiased bg-white dark:bg-main scroll-smooth">
    <%- include('components/Navigation.html') %>
    <%- include(typeof content !== 'undefined' ? content : 'content/content-home.html') %>
    <%- include('components/Footer.html') %>
    <script type="module" src="./js/app.js"></script>
    <script type="module" src="./js/script.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

How Does It Work?

The <%- include(...) %> syntax dynamically injects the specified EJS component into the layout. This approach keeps the code modular and reusable .

Header Component (header.html)

<head>
  <link rel="manifest" href="./manifest.json">
  <meta name="msapplication-TileColor" content="#ffffff">
  <meta name="msapplication-TileImage" content="./ms-icon-144x144.png">
  <meta name="theme-color" content="#ffffff">
  <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon">
  <link rel="stylesheet" href="./main.css">
  <title></title>
</head>
Enter fullscreen mode Exit fullscreen mode

The header.html file contains the HTML for the site’s header. Here’s the full code:

Navigation Component (navigation.html)

The navigation.html file contains the site’s navigation menu. Here’s the full code:

<!-- start the logo, desktop-nav, darkmode button, contact button, nav-icon & mobile-nav  -->
<header id="wrapperNavBar"
  class="sticky top-0 z-10 bg-white p-[25px] drop-shadow-xl blurGrad backdrop-blur-lg dark:bg-main dark:drop-shadow-2xl dark:border-b dark:border-slate-900/20">
  <div id="NavBar" class="flex justify-between max-w-4xl mx-auto">
    <!-- start the logo -->
    <div id="logo" class="z-40 drop-shadow-md">
      <a href="index.html">
        <img src="./img/icons/icon-dgpondcom.png" alt="Logo" id="DGPond" class="w-auto h-auto max-w-full">
      </a>
    </div>
    <%- include('components/nav-desktop.html') %>
    <%- include('components/nav-mobile.html') %>
  </div>
</header>
Enter fullscreen mode Exit fullscreen mode

Footer Component (footer.html)

The footer.html file contains the site’s footer. Here’s the full code:

<!-- start the footer section -->
<footer class="flex flex-col items-center max-w-3xl mx-auto my-10 border-t dark:border-slate-800/60">
  <div class="flex max-w-xs pt-5 mx-auto -mt-10 overflow-hidden drop-shadow-sm grayscale opacity-80">
    <img src="./img/icons/logo-footer.png" width="35" height="35" alt="bottom-logo" class="">
  </div>
  <div class="mt-5">
    <ul class="flex flex-wrap justify-between max-w-screen-md mx-auto text-sm font-navigation text-slate-400">
      <li class="mx-2">
        <a class="text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:text-gray-300 dark:hover:text-white"
          href="#">
          Home
        </a>
      </li>
      <li class="mx-2">
        <a class="text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:text-gray-300 dark:hover:text-white"
          href="#">
          About
        </a>
      </li>
      <li class="mx-2">
        <a class="text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:text-gray-300 dark:hover:text-white"
          href="#">
          Blog
        </a>
      </li>
      <li class="mx-2">
        <a class="text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:text-gray-300 dark:hover:text-white"
          href="#">
          Credits
        </a>
      </li>
    </ul>
  </div>
  <div class="flex items-center justify-between max-w-xs pt-8 mx-auto">
    <a href="#" class="ml-4 text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:hover:text-white">
      <svg width="20" height="20" fill="currentColor"
        class="text-xl transition-colors duration-200 hover:text-gray-800 dark:hover:text-white" viewBox="0 0 1792 1792"
        xmlns="http://www.w3.org/2000/svg">
        <path
          d="M1684 408q-67 98-162 167 1 14 1 42 0 130-38 259.5t-115.5 248.5-184.5 210.5-258 146-323 54.5q-271 0-496-145 35 4 78 4 225 0 401-138-105-2-188-64.5t-114-159.5q33 5 61 5 43 0 85-11-112-23-185.5-111.5t-73.5-205.5v-4q68 38 146 41-66-44-105-115t-39-154q0-88 44-163 121 149 294.5 238.5t371.5 99.5q-8-38-8-74 0-134 94.5-228.5t228.5-94.5q140 0 236 102 109-21 205-78-37 115-142 178 93-10 186-50z">
        </path>
      </svg>
    </a>
    <a href="#" class="ml-4 text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:hover:text-white">
      <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"
        class="text-xl transition-colors duration-200 hover:text-gray-800 dark:hover:text-white"
        viewBox="0 0 1792 1792">
        <path
          d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5q0 251-146.5 451.5t-378.5 277.5q-27 5-40-7t-13-30q0-3 .5-76.5t.5-134.5q0-97-52-142 57-6 102.5-18t94-39 81-66.5 53-105 20.5-150.5q0-119-79-206 37-91-8-204-28-9-81 11t-92 44l-38 24q-93-26-192-26t-192 26q-16-11-42.5-27t-83.5-38.5-85-13.5q-45 113-8 204-79 87-79 206 0 85 20.5 150t52.5 105 80.5 67 94 39 102.5 18q-39 36-49 103-21 10-45 15t-57 5-65.5-21.5-55.5-62.5q-19-32-48.5-52t-49.5-24l-20-3q-21 0-29 4.5t-5 11.5 9 14 13 12l7 5q22 10 43.5 38t31.5 51l10 23q13 38 44 61.5t67 30 69.5 7 55.5-3.5l23-4q0 38 .5 88.5t.5 54.5q0 18-13 30t-40 7q-232-77-378.5-277.5t-146.5-451.5q0-209 103-385.5t279.5-279.5 385.5-103zm-477 1103q3-7-7-12-10-3-13 2-3 7 7 12 9 6 13-2zm31 34q7-5-2-16-10-9-16-3-7 5 2 16 10 10 16 3zm30 45q9-7 0-19-8-13-17-6-9 5 0 18t17 7zm42 42q8-8-4-19-12-12-20-3-9 8 4 19 12 12 20 3zm57 25q3-11-13-16-15-4-19 7t13 15q15 6 19-6zm63 5q0-13-17-11-16 0-16 11 0 13 17 11 16 0 16-11zm58-10q-2-11-18-9-16 3-14 15t18 8 14-14z">
        </path>
      </svg>
    </a>
    <a href="#" class="ml-4 text-gray-400 transition-colors duration-200 hover:text-gray-800 dark:hover:text-white">
      <svg width="20" height="20" fill="currentColor"
        class="text-xl transition-colors duration-200 hover:text-gray-800 dark:hover:text-white" viewBox="0 0 1792 1792"
        xmlns="http://www.w3.org/2000/svg">
        <path
          d="M477 625v991h-330v-991h330zm21-306q1 73-50.5 122t-135.5 49h-2q-82 0-132-49t-50-122q0-74 51.5-122.5t134.5-48.5 133 48.5 51 122.5zm1166 729v568h-329v-530q0-105-40.5-164.5t-126.5-59.5q-63 0-105.5 34.5t-63.5 85.5q-11 30-11 81v553h-329q2-399 2-647t-1-296l-1-48h329v144h-2q20-32 41-56t56.5-52 87-43.5 114.5-15.5q171 0 275 113.5t104 332.5z">
        </path>
      </svg>
    </a>
  </div>
</footer>
Enter fullscreen mode Exit fullscreen mode

Main Entry Point (index.html)

Finally, the index.html file serves as the entry point for the application. It simply includes the base.html layout:

<!-- src/index.html -->
<%- include('layouts/base.html') %>
Enter fullscreen mode Exit fullscreen mode

Step 4.5: Test the Setup

After modularizing the code, I tested the setup by running the development server:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Vite launched the server at http://localhost:5173, and I verified that:

  • The Tailwind styles were applied correctly.
  • All components rendered as expected.
  • Hot module replacement (HMR) worked when editing files .

What Have We Achieved So Far?

Modularizing my code using vite-plugin-ejs made my project easier to maintain and scale. By breaking the HTML into reusable components, I reduced duplication and improved readability. Plus, the use of EJS templates ensured that changes to shared components (like the header or footer) were reflected across all pages automatically .


5. Update NPM Scripts

With my project now modularized and Vite fully integrated, it was time to update my package.json scripts to align with the new workflow. This step ensures that I can efficiently run, build, and preview my project using Vite’s powerful features. Here’s how I approached it:

Step 5.1: Analyze My Current Scripts

Before making changes, I reviewed my existing scripts section in package.json. Here’s what I had:

"scripts": {
  "dev": "tailwindcss -i ./src/main.css -o ./dist/style.css",
  "build": "tailwindcss -i ./src/main.css -o ./dist/style.css",
  "watch": "tailwindcss -i ./src/main.css -o ./dist/style.css --watch",
  "compress": "npx tailwindcss -o ./dist/style.css --minify",
  "start": "live-server ./dist"
}
Enter fullscreen mode Exit fullscreen mode

While these scripts worked fine for my TailwindCSS setup, they didn’t leverage Vite’s capabilities. For example, the live-server command was unnecessary since Vite includes a built-in development server .

Step 5.2: Update Scripts for Vite

I updated my scripts section to incorporate Vite’s commands while keeping some of my existing workflows intact. Here’s the revised version:

"scripts": {
  "dev": "vite", // Start the Vite development server with hot module replacement (HMR)
  "build": "vite build", // Build the project for production
  "preview": "vite preview", // Preview the production build locally
  "tailwind:watch": "tailwindcss -i ./src/main.css -o ./dist/style.css --watch", // Watch and rebuild Tailwind styles during development
  "start": "npm run dev" // Alias for starting the development server
}
Enter fullscreen mode Exit fullscreen mode

What Do These Scripts Do?

  • npm run dev: Starts the Vite development server, enabling HMR for a faster and more interactive development experience .
  • npm run build: Creates an optimized production build in the dist folder, ready for deployment.
  • npm run preview: Allows me to preview the production build locally before deploying it. This is particularly useful for testing performance and functionality .
  • npm run tailwind:watch: Keeps my existing TailwindCSS workflow intact by watching for changes and rebuilding styles as needed.
  • npm run start: Acts as a convenient alias for starting the development server.

Step 5.3: Test the Updated Scripts

To ensure everything worked as expected, I tested each script:

  1. Start the Development Server

    I ran the following command:

    npm run dev
    

    Vite launched the development server at http://localhost:5173, and my modular components rendered perfectly. Hot module replacement (HMR) worked seamlessly, updating the browser instantly when I made changes .

  2. Build for Production

    Next, I built the project for production:

    npm run build
    

    Vite generated an optimized build in the dist folder. The output included minified CSS and JavaScript files, ensuring high performance .

  3. Preview the Production Build

    To verify the production build, I ran:

    npm run preview
    

    Vite served the production build locally, allowing me to test its functionality and performance before deploying it.

  4. Watch Tailwind Styles

    Finally, I tested the tailwind:watch script:

    npm run tailwind:watch
    

    This ensured that my Tailwind styles were rebuilt automatically whenever I made changes to my CSS or HTML files .

Why This Step Matters

Updating my NPM scripts streamlined my workflow and aligned it with Vite’s best practices. By replacing live-server with Vite’s built-in development server, I eliminated redundancy and improved performance. Additionally, the preview script gave me confidence in my production builds before deploying them. For further reading on Vite’s capabilities, I recommend checking out the official Vite documentation .


6. Adjust TailwindCSS Setup

With Vite and vite-plugin-ejs now fully integrated into my project, it was time to adjust my TailwindCSS setup to ensure it worked seamlessly with the new modular structure. This step involved updating the tailwind.config.js file, ensuring proper content scanning, and optimizing the workflow for both development and production environments.

Step 6.1: Update tailwind.config.mjs

The first thing I did was update the content property in my tailwind.config.js file. Since I had modularized my HTML into .html files, I needed to ensure Tailwind scanned these files for class names .

Here’s the updated configuration:

const colors = require("tailwindcss/colors");

/** @type {import('tailwindcss').Config} */
module.exports = {
  // Scan for Tailwind classes in .html files
  content: [
    "./src/**/*.{html,ejs,js,ts}", // Include all HTML, EJS, and JS files in the src folder
  ],
  darkMode: "class",
  theme: {
    screens: {
      sm: "480px",
      md: "768px",
      lg: "976px",
      xl: "1440px",
    },
    extend: {
      animation: {
        blob: "blob 7s infinite",
      },
      colors: {
        savoryWhite: "hsl(0, 36%, 95%)",
        mutedWhite: "hsl(224, 16%, 54%)",
        whiteHover: "hsl(0, 36%, 95%)",
        pinkHeading: "hsl(279, 48%, 54%)",
        pinkSubHeading: "hsl(293, 43%, 55%)",
        blackSubHeading: "hsl(221, 39%, 11%)",
        deepBlueSubHeading: "hsl(229, 38%, 40%)",
        fuchsia: colors.fuchsia,
      },
      keyframes: {
        blob: {
          "0%": {
            transform: "translate(0px, 0px) scale(1)",
          },
          "33%": {
            transform: "translate(30px, -50px) scale(1.1)",
          },
          "66%": {
            transform: "translate(-20px, 20px) scale(0.9)",
          },
          "100%": {
            transform: "translate(0px, 0px) scale(1)",
          },
        },
      },
      backgroundColor: {
        main: "hsl(215, 45%, 14%)",
      },
    },
    fontFamily: {
      navigation: ["Poppins", "sans-serif"],
    },
  },
  plugins: [
    require("@tailwindcss/typography"),
    require("@tailwindcss/forms"),
    require("@tailwindcss/line-clamp"),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Why This Change?

By including .html & .ejs files in the content array, I ensured that Tailwind scanned my modular components for class names. This prevents unused styles from being purged during production builds .

Step 6.2: Optimize TailwindCSS Workflow

To make my TailwindCSS workflow more efficient, I kept the tailwind:watch script in my package.json. This script watches for changes in my CSS and rebuilds styles automatically during development:

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  "tailwind:watch": "tailwindcss -i ./src/main.css -o ./dist/style.css --watch", // Watch and rebuild Tailwind styles
  "start": "npm run dev"
}
Enter fullscreen mode Exit fullscreen mode

This ensures that my styles are always up-to-date, even when working with modular both .html & .ejs files.

Step 6.3: Test the TailwindCSS Integration

To verify that TailwindCSS was working correctly, I performed the following tests:

  1. Development Environment

    I ran the tailwind:watch script alongside the Vite development server:

    npm run tailwind:watch & npm run dev
    

    This allowed me to see real-time updates to my styles as I edited my .html components .

  2. Production Build

    Next, I built the project for production:

    npm run build
    

    Vite generated an optimized build in the dist folder, including minified CSS and JavaScript files. I verified that all Tailwind classes were included and applied correctly .

  3. Preview the Production Build

    Finally, I previewed the production build locally:

    npm run preview
    

    The site rendered perfectly, with all styles intact and no missing classes.

Why This Step Matters

Adjusting my TailwindCSS setup ensured that it worked seamlessly with my new modular structure. By updating the content property in tailwind.config.js, I avoided issues with purging unused styles during production builds. Additionally, the tailwind:watch script streamlined my development workflow, making it easier to iterate on designs .

For further reading on integrating TailwindCSS with Vite, I recommend checking out this GitHub repository .


7. Test the Development Workflow

With my project now fully integrated with Vite, vite-plugin-ejs, and TailwindCSS, it was time to test the development workflow. This step ensured that everything worked as expected—both in development and production environments. Here’s how I approached it:

Step 7.1: Start the Development Server

I began by starting the Vite development server using the dev script:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Vite launched the server at http://localhost:5173, and my modular components rendered perfectly . The hot module replacement (HMR) feature worked seamlessly, updating the browser instantly whenever I made changes to my .html files or CSS.

What Did I Test?

  • HTML Components: I verified that all modular components (header.html, navigation.html, content.html, and footer.html) were rendered correctly within the base.html layout.
  • Tailwind Styles: I checked that all Tailwind classes were applied as expected, ensuring no styles were missing or broken.
  • Dynamic Updates: I edited one of my .html files (e.g., header.html) and confirmed that the changes appeared immediately in the browser without requiring a full reload .

Step 7.2: Test TailwindCSS Watch Mode

To ensure my styles were always up-to-date during development, I ran the tailwind:watch script alongside the Vite server:

npm run tailwind:watch & npm run dev
Enter fullscreen mode Exit fullscreen mode

This combination allowed me to see real-time updates to my styles as I edited my .html components or main.css file . For example, when I added a new Tailwind class like text-pinkHeading to the header.html file, the change was reflected instantly in the browser.

Step 7.3: Build for Production

Next, I tested the production build process by running:

npm run build
Enter fullscreen mode Exit fullscreen mode

Vite generated an optimized build in the dist folder, including minified CSS and JavaScript files. To verify the output, I inspected the dist folder and confirmed that:

  • All .html components were compiled into static HTML files.
  • TailwindCSS purged unused styles, resulting in a smaller CSS file size .
  • The production build included all necessary assets (e.g., images, fonts).

Here’s an example of the optimized index.html file generated in the dist folder:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>All Things Digital</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <!-- Header Component -->
  <header class="bg-savoryWhite text-blackSubHeading py-4">
    <div class="container mx-auto flex justify-between items-center">
      <h1 class="text-pinkHeading font-bold text-2xl">All Things Digital</h1>
      <button id="menu-toggle" class="hamburger">
        <span class="hamburger-top"></span>
        <span class="hamburger-middle"></span>
        <span class="hamburger-bottom"></span>
      </button>
    </div>
  </header>

  <!-- Navigation Component -->
  <nav class="hidden md:flex space-x-6 text-mutedWhite">
    <a href="/" class="hover:text-whiteHover">Home</a>
    <a href="/about.html" class="hover:text-whiteHover">About</a>
    <a href="/blog.html" class="hover:text-whiteHover">Blog</a>
    <a href="/contact.html" class="hover:text-whiteHover">Contact</a>
  </nav>

  <!-- Content Component -->
  <main>
    <section class="py-8">
      <div class="container mx-auto">
        <h2 class="text-pinkSubHeading text-3xl font-bold mb-4">Welcome to All Things Digital</h2>
        <p class="text-mutedWhite">
          Explore the latest trends, tips, and insights in the digital world.
        </p>
      </div>
    </section>
  </main>

  <!-- Footer Component -->
  <footer class="bg-main text-white py-6">
    <div class="container mx-auto text-center">
      <p>&copy; 2025 All Things Digital. All rights reserved.</p>
    </div>
  </footer>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Why Is This Important?

The production build ensures that my site is optimized for performance, with minimal file sizes and no unnecessary code. This is critical for delivering a fast and responsive user experience .

Step 7.4: Preview the Production Build

To test the production build locally, I ran:

npm run preview
Enter fullscreen mode Exit fullscreen mode

Vite served the production build at http://localhost:4173, allowing me to verify its functionality and performance. I performed the following checks:

  • Rendering: Ensured all components and styles rendered correctly.
  • Performance: Verified that the site loaded quickly, thanks to Vite’s optimized build process.
  • Responsiveness: Tested the site on different screen sizes to confirm that Tailwind’s responsive utilities worked as expected .

Step 7.5: Document Observations

Throughout the testing process, I documented my observations and added comments to relevant files. For example, here’s a comment I added to my vite.config.js file:

// Import required modules for Vite configuration
import { defineConfig } from 'vite'
import tailwindcss from 'tailwindcss'
import autoprefixer from 'autoprefixer'
import imagemin from 'vite-plugin-imagemin'
import htmlMinifier from 'vite-plugin-html-minifier'
import { ViteEjsPlugin } from 'vite-plugin-ejs'
import { resolve } from 'path'

/**
 * Vite configuration for the project.
 *
 * This config sets up:
 * - Project root directory
 * - Build output settings
 * - PostCSS plugins (Tailwind CSS and Autoprefixer)
 * - Images, HTML, CSS, and JavaScript compressions using Vite plugins
 * - EJS templating for HTML modularity
 */
export default defineConfig({
  // Set the project root directory to './src'
  root: './src',

  // Build configuration
  build: {
    // Output directory for built files (relative to project root)
    outDir: '../dist',
    // Clear the output directory before each build
    emptyOutDir: true,
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'src/index.html'),
        about: resolve(__dirname, 'src/about.html'),
        contact: resolve(__dirname, 'src/contact.html'),
        blog: resolve(__dirname, 'src/blog.html'),
        category: resolve(__dirname, 'src/category.html'),
      },
    },
  },

  // CSS processing configuration
  css: {
    // PostCSS configuration
    postcss: {
      plugins: [tailwindcss, autoprefixer],
    },
  },

  // Plugins configuration
  plugins: [
    // Image minification
    imagemin({
      pngquant: {
        quality: [0.7, 0.9],
        speed: 4,
      },
    }),
    // HTML minification
    htmlMinifier({
      minify: true,
      collapseWhitespace: true,
      keepClosingSlash: true,
      removeComments: true,
      removeRedundantAttributes: true,
      removeScriptTypeAttributes: true,
      removeStyleLinkTypeAttributes: true,
      useShortDoctype: true,
    }),
    // EJS templating for HTML
    ViteEjsPlugin(),
  ],

  server: {
    watch: {
      usePolling: true,
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

Why Add Comments?

Comments make the code easier to understand for future me—or anyone else who might work on the project. They also serve as a reminder of why certain decisions were made .

Why This Step Matters

Testing the development workflow ensured that my project was ready for both development and production environments. By verifying that Vite, vite-plugin-ejs, and TailwindCSS worked seamlessly together, I gained confidence in the stability and performance of my site . For further reading on conducting effective tests, I recommend this step-by-step guide for knowledge checks .


8. Build for Production

With my development workflow thoroughly tested, it was time to focus on building the project for production. This step ensures that my site is optimized for performance, with minimal file sizes and no unnecessary code. Here’s how I approached it:

Step 8.1: Run the Production Build Command

I started by running the build script, which generates an optimized production build in the dist folder:

npm run build
Enter fullscreen mode Exit fullscreen mode

Vite processed my modular .html & .ejs components, TailwindCSS styles, and JavaScript files, bundling them into a clean and efficient structure . The output included:

  • Minified CSS and JavaScript files.
  • Static HTML files compiled from my .html & .ejs templates.
  • All necessary assets (e.g., images, fonts) copied to the dist folder.

Here’s an example of the optimized index.html file generated in the dist folder:

<!-- dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>All Things Digital</title>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  <!-- Header Component -->
  <header class="bg-savoryWhite text-blackSubHeading py-4">
    <div class="container mx-auto flex justify-between items-center">
      <h1 class="text-pinkHeading font-bold text-2xl">All Things Digital</h1>
      <button id="menu-toggle" class="hamburger">
        <span class="hamburger-top"></span>
        <span class="hamburger-middle"></span>
        <span class="hamburger-bottom"></span>
      </button>
    </div>
  </header>

  <!-- Navigation Component -->
  <nav class="hidden md:flex space-x-6 text-mutedWhite">
    <a href="/" class="hover:text-whiteHover">Home</a>
    <a href="/about.html" class="hover:text-whiteHover">About</a>
    <a href="/blog.html" class="hover:text-whiteHover">Blog</a>
    <a href="/contact.html" class="hover:text-whiteHover">Contact</a>
  </nav>

  <!-- Content Component -->
  <main>
    <section class="py-8">
      <div class="container mx-auto">
        <h2 class="text-pinkSubHeading text-3xl font-bold mb-4">Welcome to All Things Digital</h2>
        <p class="text-mutedWhite">
          Explore the latest trends, tips, and insights in the digital world.
        </p>
      </div>
    </section>
  </main>

  <!-- Footer Component -->
  <footer class="bg-main text-white py-6">
    <div class="container mx-auto text-center">
      <p>&copy; 2025 All Things Digital. All rights reserved.</p>
    </div>
  </footer>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

Why Is This Important?

The production build ensures that my site is ready for deployment, with all assets optimized for speed and efficiency. This is critical for delivering a fast and responsive user experience .

Step 8.2: Inspect the Output Folder

After the build process completed, I inspected the dist folder to verify its contents:

  • CSS: The style.css file was minified, ensuring faster load times.
  • HTML: My modular .html components were compiled into static HTML files, making them easy to serve.
  • Assets: Images, fonts, and other assets were copied to the dist folder, ensuring they were available for use.

Here’s a snapshot of the dist folder structure:

dist/
├── assets/
├── index.html
├── about.html
├── blog.html
├── style.css
└── script.js
Enter fullscreen mode Exit fullscreen mode

What Did I Learn?

Inspecting the dist folder gave me confidence that everything was in place for deployment. Vite’s build process handled all optimizations automatically, saving me time and effort .

Step 8.3: Preview the Production Build

To test the production build locally, I ran the preview script:

npm run preview
Enter fullscreen mode Exit fullscreen mode

Vite served the production build at http://localhost:4173, allowing me to verify its functionality and performance . I performed the following checks:

  • Rendering: Ensured all components and styles rendered correctly.
  • Performance: Verified that the site loaded quickly, thanks to Vite’s optimized build process.
  • Responsiveness: Tested the site on different screen sizes to confirm that Tailwind’s responsive utilities worked as expected.

Why Use the preview Script?

The preview script lets me test the production build locally before deploying it, reducing the risk of errors or broken functionality in the live environment .

Step 8.4: Add Comments to Key Files

To make my production setup easier to understand, I added comments to key files like vite.config.js and tailwind.config.js. For example, here’s a comment I added to my vite.config.js file:

// Vite configuration for All Things Digital
import { defineConfig } from 'vite';
import postcss from './postcss.config.js'; // Reuse my existing PostCSS setup
import ejs from 'vite-plugin-ejs';

export default defineConfig({
  root: './src', // Set the root directory for my source files
  plugins: [ejs()], // Add the EJS plugin for modular components
  build: {
    outDir: '../dist', // Output directory for production builds
    emptyOutDir: true, // Clear the output directory before building
  },
  css: {
    postcss, // Use my existing PostCSS setup
  },
});
Enter fullscreen mode Exit fullscreen mode

Why Add Comments?

Comments make the code easier to understand for future me—or anyone else who might work on the project. They also serve as a reminder of why certain decisions were made .

Step 8.5: Prepare for Deployment

Finally, I prepared the dist folder for deployment. Since the production build includes all necessary assets, I simply uploaded the contents of the dist folder to my hosting provider. Tools like Netlify, Vercel, or GitHub Pages make this process seamless .

Why This Step Matters

Building for production ensures that my site is optimized for performance, with minimal file sizes and no unnecessary code. By verifying the output and testing the production build locally, I gained confidence in the stability and performance of my site .

For further reading on optimizing production builds, I recommend checking out this guide on Tyk Self-Managed .

Lessons Learnt

Integrating Vite into my project has been a transformative experience, streamlining my workflow and enhancing both development and production processes. By modularizing my code and leveraging tools like vite-plugin-ejs, I’ve created a scalable, maintainable, and efficient setup that aligns with modern best practices. This journey not only improved my site’s performance but also deepened my understanding of how cutting-edge tools like Vite can elevate web development.

Comments 0 total

    Add comment