DEV Community

Cover image for Migrating a Static HTML/CSS/JS Project to React Using Vite - A Step-by-Step Guide
Werliton Silva
Werliton Silva

Posted on

Migrating a Static HTML/CSS/JS Project to React Using Vite - A Step-by-Step Guide

This article presents a concise, academic, and practical step-by-step procedure to convert an existing static website (HTML, CSS, JavaScript) into a React application scaffolded with Vite. The goal is to provide a reproducible pathway that emphasizes conceptual clarity and pragmatic code transformations. Each step includes precise actions and example code snippets intended to be executed directly in a typical development environment.


Prerequisites

  • Node.js and npm (or Yarn) installed.
  • Basic familiarity with HTML, CSS, and vanilla JavaScript.
  • A working copy of the original static site (HTML files, CSS, JS, and assets).

Resume

res


1. Initialize a Vite React Project

  1. Create a new Vite React project in a fresh directory.
# Using npm
npm create vite@latest my-react-site -- --template react

# Or using Yarn
yarn create vite my-react-site --template react
Enter fullscreen mode Exit fullscreen mode
  1. Install dependencies and open the project in your editor.
cd my-react-site
npm install
code .
Enter fullscreen mode Exit fullscreen mode

The generated scaffold provides an entry point (index.html), a main.jsx or main.tsx (if TypeScript template chosen), an App.jsx, and a development server configured via Vite.


2. Import the Static Site into the New Structure

  1. Copy static assets (images, fonts, icons) from the original project into src/assets/ (or public/ if you want them available as static assets).

  2. Transfer CSS files into src/styles/ or convert global styles into src/index.css. If the original site used multiple CSS files, consolidate or maintain them under src/styles/.

  3. Move any plain JavaScript utilities into src/utils/ as plain modules. These will later be imported into components as needed.

Example file layout after transfer

my-react-site/
├── index.html
├── package.json
└── src/
    ├── assets/
    │   └── logo.png
    ├── styles/
    │   └── main.css
    ├── utils/
    │   └── helpers.js
    ├── main.jsx
    └── App.jsx
Enter fullscreen mode Exit fullscreen mode

3. Understand and Adapt index.html and the Entry Point

  1. Inspect the Vite index.html. It contains the root element where React will mount, e.g. <div id="root"></div>.

  2. In Vite, index.html is a template. Keep only the static HTML head content (meta tags, title, link to favicon) and let React manage the DOM under the root element.

  3. Confirm main.jsx mounts the React application:

// src/main.jsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import './styles/main.css';

createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

4. Migrate the Page Markup into App.jsx

  1. Open a representative HTML page from the original site. Copy the body structure that composes the user interface into App.jsx.

  2. Convert static HTML to JSX syntax:

    • Replace class with className.
    • Close void elements (e.g., <img />, <input />).
    • Convert inline event handlers (e.g., onclick) into React props (onClick).
    • Ensure attribute names follow JSX conventions (forhtmlFor, tabindextabIndex, etc.).

transf

Tip: To speed up conversion of large chunks of HTML to valid JSX, you can use the free online tool HTML to JSX Converter provided by Ritz078. On that site, you paste your static HTML in one panel and it instantly outputs JSX in the other panel. It also offers toggles and settings to adjust conversion behavior for React Native or SVG. (transform.tools)

  1. Example conversion
// src/App.jsx
import React from 'react'
import Header from './components/Header'
import Footer from './components/Footer'
import './styles/main.css'

function App() {
  return (
    <>
      <Header />
      <main>
        <section className="hero">
          <h1>Original Site Title</h1>
          <p>Introductory text ported from static HTML.</p>
        </section>
        {/* Additional content goes here */}
      </main>
      <Footer />
    </>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

5. Componentize the Interface

  1. Identify logical UI sections: header, footer, navigation, card/list items, hero, forms, etc.

  2. Create a src/components/ directory and implement each section as a functional component. Keep components small and focused.

Header example

// src/components/Header.jsx
import React from 'react';
import logo from '../assets/logo.png';

export default function Header() {
  return (
    <header className="site-header">
      <img src={logo} alt="Site Logo" className="logo" />
      <nav>
        <ul>
          <li><a href="#home">Home</a></li>
          <li><a href="#about">About</a></li>
        </ul>
      </nav>
    </header>
  );
}
Enter fullscreen mode Exit fullscreen mode
  1. Import and use the components from App.jsx.

6. Replace Inline Scripts with React State and Handlers

  1. Identify interactive features implemented in vanilla JavaScript (e.g., toggles, show/hide, simple tab behavior).

  2. Translate those behaviors into React using useState and handler functions.

Simple toggle example

// src/components/ToggleText.jsx
import React, { useState } from 'react';

export default function ToggleText() {
  const [visible, setVisible] = useState(true);

  return (
    <div>
      <button onClick={() => setVisible((v) => !v)}>
        {visible ? 'Hide' : 'Show'} details
      </button>
      {visible && <p>Details converted from original script.</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  1. Remove global DOM queries (document.querySelector) and inline script tags; prefer local component state and props.

7. Convert Data-Driven Lists to .map() Rendering

  1. If the original site renders repeated elements (cards, lists), encode the data as an array of objects in a module or within component state.

  2. Render the list using .map() and supply a stable key for each element.

List example

// src/data/items.js
export const items = [
  { id: 1, title: 'Item 1', text: 'Description 1' },
  { id: 2, title: 'Item 2', text: 'Description 2' }
];
Enter fullscreen mode Exit fullscreen mode
// src/components/ItemList.jsx
import React from 'react';
import { items } from '../data/items';

export default function ItemList() {
  return (
    <section>
      {items.map((item) => (
        <article key={item.id} className="card">
          <h2>{item.title}</h2>
          <p>{item.text}</p>
        </article>
      ))}
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

8. Handle Forms as Controlled Components

  1. Replace native form scripts with controlled form inputs to capture user input in React state.

Controlled form example

// src/components/SubscribeForm.jsx
import React, { useState } from 'react';

export default function SubscribeForm() {
  const [email, setEmail] = useState('');

  function handleSubmit(e) {
    e.preventDefault();
    console.log('Email submitted:', email);
    setEmail('');
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="email">Email</label>
      <input
        id="email"
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        required
      />
      <button type="submit">Subscribe</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode
  1. Replace any inline validation logic with state-driven validation or use a lightweight form library when complexity grows.

9. Integrate Utilities and External Scripts

  1. Utilities previously loaded via script tags (e.g., helper functions) should be converted into ES modules and imported where needed.

  2. For third-party libraries that manipulate the DOM directly (e.g., jQuery plugins), prefer React-compatible alternatives or wrap them carefully within useEffect to avoid conflicts with React's rendering model.


10. Style Management

  1. If the original project used global CSS, continue to use it by importing the stylesheet in main.jsx or App.jsx. For improved encapsulation, consider:
  • CSS Modules: Component.module.css and import as styles.
  • CSS-in-JS: styled-components or similar libraries.
  • Utility frameworks: Tailwind CSS.
  1. Maintain accessibility attributes (ARIA) and semantic HTML elements when converting markup.

Example CSS import

// src/main.jsx
import './styles/main.css';
Enter fullscreen mode Exit fullscreen mode

11. Optional: Introduce Client-Side Routing

  1. If the original project had multiple pages and you wish to maintain the single-page application model, install and configure a routing library.
npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode
  1. Example minimal router setup:
// src/App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}
Enter fullscreen mode Exit fullscreen mode

12. Project Organization and Best Practices

Adopt a clear folder structure to maintain scalability and readability:

src/
├── assets/
├── components/
├── data/
├── pages/
├── styles/
├── utils/
├── App.jsx
└── main.jsx
Enter fullscreen mode Exit fullscreen mode

Guiding principles:

  • Small, single-responsibility components.
  • Lift state up only when necessary.
  • Prefer props for data flow and callbacks for child-to-parent communication.
  • Use descriptive filenames and consistent naming conventions.

13. Build and Deployment

  1. Verify the production build process provided by Vite.
# build for production
npm run build

# preview production build locally
npm run preview
Enter fullscreen mode Exit fullscreen mode
  1. Deploy to modern static hosts (for example: Vercel, Netlify, or GitHub Pages). Ensure correct configuration of the host to serve index.html for client-side routing (if routing is used).

Conclusion

This guide articulates a direct, repeatable transformation from a static HTML/CSS/JS site into a component-based React application using Vite. The process emphasizes incremental migration: import assets and global styles, port markup to JSX, componentize, replace direct DOM manipulation with React state and handlers, and reorganize code for maintainability. Adhering to these steps produces a React application that preserves the original design while gaining modularity, testability, and scalability.


Refs:

Top comments (0)