Skip to main content

Overview

The frontend is a React 18 single-page application built with TypeScript, using modern patterns and best practices.

Application Entry Point

The application initializes through a standard React 18 setup:
// src/main.tsx
import { createRoot } from "react-dom/client";
import ErrorBoundary from "./components/ErrorBoundary.tsx";
import App from "./App.tsx";
import "./index.css";

createRoot(document.getElementById("root")!).render(
  <ErrorBoundary>
    <App />
  </ErrorBoundary>
);
Key Features:
  • Error boundary wraps entire app for graceful error handling
  • Root-level CSS imports for Tailwind and global styles
  • No React.StrictMode to avoid double-rendering in development

App Component Structure

// src/App.tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Toaster } from "@/components/ui/toaster";
import { Sonner } from "@/components/ui/sonner";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <TooltipProvider>
      <Toaster />
      <Sonner />
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Index />} />
          <Route path="/admin/login" element={<AdminLogin />} />
          <Route path="/admin/email-preview" element={<EmailPreview />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </BrowserRouter>
    </TooltipProvider>
  </QueryClientProvider>
);
Provider Hierarchy:
  1. QueryClientProvider - Tanstack Query for server state
  2. TooltipProvider - Radix UI tooltip context
  3. Toaster + Sonner - Dual notification systems
  4. BrowserRouter - Client-side routing

Routing Structure

Component Architecture

Directory Structure

src/components/
├── AboutSection.tsx          # Personal introduction
├── ContactSection.tsx        # Contact form with validation
├── DifferentialsSection.tsx  # Unique value propositions
├── ErrorBoundary.tsx         # Error handling component
├── Footer.tsx                # Site footer
├── GithubActivity.tsx        # GitHub contributions display
├── Header.tsx                # Navigation header
├── HeroSection.tsx           # Landing hero section
├── MarqueeSection.tsx        # Scrolling text marquee
├── NavLink.tsx               # Navigation link component
├── ProjectsSection.tsx       # Projects grid with modal viewer
├── ResumeSection.tsx         # Professional experience
├── ScrollReveal.tsx          # Scroll animation wrapper
├── SkillsSection.tsx         # Technical skills display
├── WhatsAppButton.tsx        # Floating WhatsApp button
├── WhySiteSection.tsx        # Site explanation
└── ui/                       # Radix UI components
    ├── accordion.tsx
    ├── button.tsx
    ├── sonner.tsx
    ├── toast.tsx
    ├── toaster.tsx
    ├── tooltip.tsx
    └── use-toast.ts

Component Patterns

Components fetch data directly using native fetch API, not React Query hooks.
// src/components/ProjectsSection.tsx:162-177
const [projects, setProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
  const fetchProjects = async () => {
    try {
      const response = await fetch(`${API_URL}/projects`);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const data = await response.json();
      setProjects(Array.isArray(data) ? data : []);
    } catch (error) {
      console.error('Erro ao carregar projetos:', error);
    } finally {
      setLoading(false);
    }
  };
  fetchProjects();
}, []);
API URL Configuration:
const API_URL = import.meta.env.VITE_API_URL || '/api';

State Management

Local Component State

Most state is managed locally with useState:
const [projects, setProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(true);
const [openImage, setOpenImage] = useState<string | null>(null);

No Global State

The application doesn’t use Redux, Zustand, or other global state libraries. State is either:
  • Local to components
  • Fetched from API on mount
  • Passed via props when needed

Authentication State

Admin token stored in localStorage:
// AdminLogin.tsx (pattern)
if (response.ok) {
  const { token } = await response.json();
  localStorage.setItem('adminToken', token);
  navigate('/admin/email-preview');
}

Styling Architecture

TailwindCSS Configuration

// tailwind.config.ts
export default {
  darkMode: ["class"],
  content: ["./index.html", "./src/**/*.{ts,tsx}"],
  theme: {
    extend: {
      colors: {
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: "hsl(var(--primary))",
        // ... CSS variables for theming
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out",
      },
    },
  },
  plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
};

Component Styling Patterns

  1. Utility-First: Tailwind classes directly in JSX
  2. CSS Variables: Theme colors defined in :root and [data-theme]
  3. Class Variance Authority: Used in UI components for variant props
  4. Tailwind Merge: cn() utility prevents class conflicts
// src/lib/utils.ts
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs) {
  return twMerge(clsx(inputs));
}

TypeScript Types

Project Interface

// src/components/ProjectsSection.tsx:8-22
interface ProjectLink {
  url: string;
  label: string;
}

interface Project {
  id: number;
  title: string;
  description: string;
  tags: string[];
  image: string | null;
  links: ProjectLink[];
  github: string | null;
  order: number;
}

Type Safety

  • Vite env types: src/vite-env.d.ts for import.meta.env
  • Strict mode: Enabled in tsconfig.json
  • Path aliases: @/./src/ configured in both Vite and TSConfig

Build Configuration

// vite.config.ts
export default defineConfig(({ mode }) => ({
  server: {
    host: "::",
    port: 8080,
    proxy: {
      "/api": {
        target: "http://localhost:3001",
        changeOrigin: true,
      },
    },
  },
  plugins: [
    react(),
    mode === "development" && componentTagger()
  ].filter(Boolean),
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
}));
Key Features:
  • React SWC plugin for fast refresh
  • Component tagger in development only
  • API proxy to backend during dev
  • Path alias resolution

Testing Setup

// vitest.config.ts
export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
  },
});
Example Test:
// src/components/ErrorBoundary.test.tsx
import { render, screen } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';

test('renders children when no error', () => {
  render(
    <ErrorBoundary>
      <div>Test Content</div>
    </ErrorBoundary>
  );
  expect(screen.getByText('Test Content')).toBeInTheDocument();
});

Next Steps

Backend Architecture

Explore Express server structure and API endpoints

Component Reference

Detailed guide for each component

Build docs developers (and LLMs) love