Apoya mi contenido: 

Tabla de contenido

Tutorial: construir una SPA con React y Vite paso a paso

En esta guía aprenderás a crear una Single Page Application (SPA) moderna con React y Vite, incorporando routing, estado global, peticiones a API, carga diferida y buenas prácticas para producción.

Requisitos

  • Node.js 18+ y npm/pnpm/yarn instalados.
  • Conocimientos básicos de JavaScript y React.

Paso 1 — Crear el proyecto

# con npm
npm create vite@latest my-spa -- --template react-swc
cd my-spa
npm install
npm run dev

Abre http://localhost:5173 para verificar.

Paso 2 — Estructura recomendada

my-spa/
├─ src/
│  ├─ components/
│  ├─ hooks/
│  ├─ pages/
│  ├─ context/
│  ├─ styles/
│  ├─ App.jsx
│  └─ main.jsx
├─ public/
└─ index.html

Paso 3 — Instalar el router

npm i react-router-dom

src/main.jsx

import React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import App from "./App.jsx";
import Home from "./pages/Home.jsx";
import About from "./pages/About.jsx";
import User from "./pages/User.jsx";
import NotFound from "./pages/NotFound.jsx";
import "./styles/global.css";

createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="users/:id" element={<User />} />
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </BrowserRouter>
  </React.StrictMode>
);

src/App.jsx (layout + navegación)

import { Outlet, NavLink } from "react-router-dom";

export default function App() {
  const link = ({ isActive }) => ({
    padding: "8px 12px",
    borderRadius: 8,
    textDecoration: "none",
    color: isActive ? "#111" : "#555",
    background: isActive ? "#e5e7eb" : "transparent"
  });

  return (
    <div style={{ maxWidth: 960, margin: "0 auto", padding: 24 }}>
      <nav style={{ display: "flex", gap: 12, marginBottom: 24 }}>
        <NavLink style={link} to="/" end>Inicio</NavLink>
        <NavLink style={link} to="/about">Acerca</NavLink>
        <NavLink style={link} to="/users/1">Usuario demo</NavLink>
      </nav>
      <Outlet />
    </div>
  );
}

Páginas básicas

src/pages/Home.jsx

export default function Home() {
  return (<h1>Bienvenido a la SPA con React + Vite</h1>);
}

src/pages/About.jsx

export default function About() {
  return (<p>Esta es una SPA creada con Vite, React y React Router.</p>);
}

src/pages/NotFound.jsx

export default function NotFound() {
  return (<h2>404 — Página no encontrada</h2>);
}

Paso 4 — Consumir una API con un hook

Creamos una variable de entorno y un hook reutilizable.

.env.local

VITE_API_URL=https://jsonplaceholder.typicode.com

src/hooks/useFetch.js

import { useEffect, useState } from "react";

export function useFetch(path) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    const url = `${import.meta.env.VITE_API_URL}${path}`;

    (async () => {
      try {
        setLoading(true);
        const res = await fetch(url, { signal: controller.signal });
        if (!res.ok) throw new Error(`Error ${res.status}`);
        setData(await res.json());
      } catch (e) {
        if (e.name !== "AbortError") setError(e);
      } finally {
        setLoading(false);
      }
    })();

    return () => controller.abort();
  }, [path]);

  return { data, loading, error };
}

src/pages/User.jsx

import { useParams } from "react-router-dom";
import { useFetch } from "../hooks/useFetch";

export default function User() {
  const { id } = useParams();
  const { data: user, loading, error } = useFetch(`/users/${id}`);

  if (loading) return <p>Cargando...</p>;
  if (error) return <p>Hubo un error: {error.message}</p>;

  return (
    <article>
      <h2>Usuario #{user.id}</h2>
      <p><strong>Nombre:</strong> {user.name}</p>
      <p><strong>Email:</strong> {user.email}</p>
    </article>
  );
}

Paso 5 — Estado global con Context (ejemplo)

src/context/ThemeContext.jsx

import { createContext, useContext, useState } from "react";
const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");
  const toggle = () => setTheme(t => (t === "light" ? "dark" : "light"));
  return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>;
}

export const useTheme = () => useContext(ThemeContext);

En main.jsx envuelve el árbol:

import { ThemeProvider } from "./context/ThemeContext.jsx";
/* ... */
createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <ThemeProvider>
      <BrowserRouter>{/* rutas */}</BrowserRouter>
    </ThemeProvider>
  </React.StrictMode>
);

Paso 6 — Code splitting (carga diferida)

import { lazy, Suspense } from "react";
const About = lazy(() => import("./pages/About.jsx"));

/* en tus Routes */
<Route path="about" element={
  <Suspense fallback={<p>Cargando sección...</p>}>
    <About />
  </Suspense>
} />

Paso 7 — Estilos

  • CSS Modules (nativo en Vite) o Tailwind CSS si prefieres utilidades.
  • Importa un global.css con reglas base y variables.

Paso 8 — Buenas prácticas

  • ESLint + Prettier (npm i -D eslint prettier).
  • Rutas relativas estables y alias en vite.config.js.
  • Manejo de errores y estados vacíos en cada vista.

Paso 9 — Build y despliegue

npm run build
npm run preview
  • El build queda en /dist.
  • Para SPAs, configura el fallback a /index.html en tu hosting/CDN (Netlify: archivo _redirects con /* /index.html 200).

Conclusión

Con Vite y React puedes levantar una SPA rápida, modular y lista para producción. Añadiendo router, hooks para datos, contexto y carga diferida, obtienes una base sólida para cualquier proyecto profesional.

👉 ¿Quieres que creemos tu SPA con React y Vite de forma profesional?
Solicítalo aquí.

¡Comunícate con nosotros!

Ads Blocker Image Powered by Code Help Pro

Bloqueador de anuncios detectado!!!

 Por favor, apóyanos desactivando este bloqueador de anuncios para seguir creando contenido que te gusta 🙏🏼

Powered By
100% Free SEO Tools - Tool Kits PRO