npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

buildfast

v1.0.9

Published

![banner](https://github.com/nappalm/builfast/raw/master/public/banner.jpg)

Downloads

226

Readme

banner

BuildFast | AI-Driven Boilerplate for Faster Launch and Scale of Frontend SPAs

BuildFast is a production-ready boilerplate that accelerates the development of modern React applications. Built with TypeScript, Vite, and industry best practices, it allows you to focus on building unique features while the boilerplate handles architecture, configuration, and development tooling.

Table of Contents

  1. About the Project
  2. Quick Start
  3. Directory Organization
  4. Screaming Architecture
  5. Testing
  6. Code Conventions
  7. Environment Variables
  8. Available Scripts
  9. Deployment
  10. Best Practices
  11. Troubleshooting

1. About the Project

BuildFast is a production-ready boilerplate designed to accelerate the development of modern frontend applications. It implements a scalable architecture based on the Screaming Architecture pattern, where the project structure clearly reflects business capabilities rather than technical types.

🎯 Key Features

  • TypeScript First: Static typing for greater reliability and better DX
  • Feature-based Architecture: Self-contained and scalable modules
  • Integrated Testing: Vitest + Testing Library configured and ready
  • Quality Gates: Husky, ESLint, Prettier, and Commitlint preconfigured
  • Auth Ready: Mocked authentication screens ready for UI iteration
  • Modern UI: Semantic HTML ready for custom styling
  • API Management: React Query for data caching and synchronization

🛠️ Tech Stack

Core

  • React 18: UI library with latest features
  • TypeScript: Static typing for JavaScript
  • Vite: Ultra-fast build tool with HMR

Routing & Navigation

State & Data

UI

Forms & Validation

Backend

  • Stripe: Integrated payment processing

Testing

Code Quality

Code Generation

2. Quick Start

Prerequisites

  • Node.js: >= 18.x
  • npm or yarn or pnpm
  • Git: For version control
  • Stripe account (optional, for payments)

Installation

1. Clone or use the boilerplate

# Option 1: Clone the repository
git clone https://github.com/nappalm/buildfast.git my-project
cd my-project

# Option 2: Use as template on GitHub
# Click "Use this template" on GitHub

# Option 3: Use npx (if published)
npx buildfast create my-project

2. Install dependencies

npm install

3. Configure environment variables

Copy the example files and configure your credentials:

# The project uses separate files per environment
# .env.development - For local development
# .env.production - For production

File .env.development:

# Stripe (optional - for payment processing)
VITE_APP_STRIPE_PUBLISHABLE_KEY=pk_test_...

# Regional configuration
locale=enUS

Note: All environment variables for the frontend must start with VITE_APP_ to be accessible in the code.

4. Start the development server

npm run dev

The application will be available at http://localhost:5173

Basic Project Structure

buildfast/
├── src/
│   ├── app/              # Global configuration (providers, router)
│   ├── features/         # Business modules (auth, home, settings)
│   ├── lib/             # External library configuration
│   └── shared/          # Shared code (components, hooks, utils)
├── public/              # Static assets
└── .husky/             # Git hooks

3. Directory Organization

The directory structure follows the principle of organization by domain, not by technical type. This means everything related to a business capability (feature) lives together.

Complete Structure

buildfast/
├── src/
│   ├── app/                    # 🎯 Global application configuration
│   │   ├── main.tsx           # Entry point - Mounts React to DOM
│   │   ├── providers.tsx      # Global providers composition
│   │   └── router.tsx         # Main application router
│   │
│   ├── features/              # 🎨 Self-contained domain modules
│   │   ├── auth/             # Authentication feature
│   │   │   ├── components/   # AuthProvider, ProtectedRoute, SignInForm, etc.
│   │   │   ├── hooks/        # useAuth, useAuthenticatedUser
│   │   │   ├── pages/        # SignInPage, SignUpPage
│   │   │   ├── router/       # Auth routes and paths
│   │   │   ├── utils/        # Auth-specific helpers
│   │   │   └── index.ts      # ⭐ Public API - Only exports
│   │   │
│   │   └── home/             # Home/landing feature
│   │
│   ├── lib/                   # 🔧 Third-party library configuration
│   │   ├── axios/            # Axios configuration and interceptors
│   │   ├── react-query/      # React Query client
│   │   ├── shadcn/           # shadcn/ui components and Tailwind v4 setup
│   │   ├── stripe/           # Stripe configuration
│   │   └── test/             # Testing setup (Vitest)
│   │
│   └── shared/               # 🌐 Generic shared code
│       ├── components/       # Button, Modal, Layout - base components
│       ├── constants/        # Global constants
│       ├── hooks/           # useLocalStorage, useDebounce, etc.
│       ├── services/        # Shared services (non-feature-specific)
│       ├── utils/           # Generic helpers
│       └── index.ts         # Shared public API
│
├── public/                   # Static assets (favicon, images, etc.)
├── .husky/                   # Git hooks
└── scripts/                  # Utility scripts (build, deploy, etc.)

Anatomy of a Feature

Each feature is a self-contained vertical module that includes everything needed to function:

src/features/products/
├── components/                    # 🧩 Feature UI components
│   ├── ProductCard.tsx
│   ├── ProductList.tsx
│   ├── ProductForm.tsx
│   └── ProductFilters.tsx
│
├── hooks/                        # 🪝 Custom hooks (orchestration layer)
│   ├── useProducts.ts           # Fetching and caching with React Query
│   ├── useProduct.ts            # Single product
│   ├── useCreateProduct.ts      # Mutation to create
│   └── useUpdateProduct.ts      # Mutation to update
│
├── pages/                        # 📄 Feature pages/views
│   ├── ProductsListPage.tsx
│   ├── ProductDetailPage.tsx
│   ├── CreateProductPage.tsx
│   └── EditProductPage.tsx
│
├── router/                       # 🛤️ Route configuration
│   ├── index.ts                 # Re-export of paths and router
│   ├── paths.ts                 # Route constants
│   │   export const PRODUCTS_PATHS = {
│   │     ROOT: '/products',
│   │     DETAIL: '/products/:id',
│   │     CREATE: '/products/new',
│   │     EDIT: '/products/:id/edit',
│   │   }
│   └── router.tsx               # React Router configuration
│
├── services/                     # 🔌 API calls (async functions)
│   └── products.service.ts
│       - getProducts()
│       - getProduct(id)
│       - createProduct(data)
│       - updateProduct(id, data)
│       - deleteProduct(id)
│
├── utils/                        # 🛠️ Feature-specific helpers
│   └── product.utils.ts         # formatPrice(), calculateDiscount(), etc.
│
└── index.ts                      # ⭐ PUBLIC API
    export { useProducts, useProduct } from './hooks'
    export { productsRouter } from './router'
    export { PRODUCTS_PATHS } from './router'
    // ❌ DO NOT export: services, internal components, utils

src/lib/ Directory

Configuration and setup of third-party libraries:

src/lib/
├── axios/
│   └── client.ts              # Axios instance + interceptors
│
├── chakra/
│   ├── theme.ts              # Theme customization
│   ├── colors.ts             # Color palette
│   └── components/           # Component overrides
│
├── react-query/
│   ├── client.ts             # Configured QueryClient
│   └── constants.ts          # Query keys, stale time, etc.
│
├── stripe/
│   └── client.ts             # Stripe configuration
│
└── test/
    ├── setup.ts              # Global test setup
    └── utils.tsx             # Testing utilities, custom render, etc.

src/shared/ Directory

Generic and reusable code that is NOT tied to any specific feature:

src/shared/
├── components/
│   ├── Button/              # Base button component
│   ├── Modal/              # Reusable modal
│   ├── Layout/             # Generic layouts
│   └── ...
│
├── hooks/
│   ├── useLocalStorage.ts  # localStorage hook
│   ├── useDebounce.ts      # Generic debounce
│   ├── useDisclosure.ts    # Open/close state management
│   └── ...
│
├── utils/
│   ├── format.ts           # formatDate, formatCurrency
│   ├── validation.ts       # Generic validators
│   └── ...
│
└── constants/
    ├── routes.ts           # Global routes
    └── config.ts           # Global configuration

Organization Rules

✅ DO

  • Keep everything related to a feature within its folder
  • Export only what's necessary in each feature's index.ts
  • Place truly generic code in shared/
  • Use absolute imports with the @/ alias (configured in Vite)

❌ DON'T

  • DO NOT import internal files from other features directly
  • DO NOT place business logic in shared/
  • DO NOT export services or internal components in the public API
  • DO NOT create circular dependencies between features

4. Screaming Architecture

BuildFast implements the Screaming Architecture pattern created by Robert C. Martin (Uncle Bob). This approach makes the project structure "scream" its business purpose rather than the technical frameworks or tools used.

Fundamental Principles

1. 🎯 Organize by Domain, Not by Technical Type

❌ Traditional Approach (by technical type):

src/
├── components/     # All components together
├── hooks/         # All hooks together
├── pages/         # All pages together
└── services/      # All services together

✅ BuildFast Approach (by domain):

src/
└── features/
    ├── products/   # Everything about products
    ├── auth/      # Everything about authentication
    └── orders/    # Everything about orders

When you see the features/ folder, you immediately understand WHAT the application does, not what technologies it's built with.

2. 🏢 Features as Self-Contained Vertical Modules

Each feature is like an independent mini-application with everything it needs:

// ✅ A feature contains everything necessary
src/features/products/
├── components/    # Product-specific UI
├── hooks/        # Product logic
├── services/     # Product API calls
├── pages/        # Product pages
└── router/       # Product routes

Benefits:

  • Scalability: Add features without affecting others
  • Maintainability: Everything related is together
  • Testability: Test features in isolation
  • Teamwork: Different developers can work on different features without conflicts

3. 🔒 Every Feature Has a Public API (index.ts)

Communication between features or from the application to a feature MUST occur exclusively through its index.ts file:

// ✅ CORRECT - Import from public API
import { useProducts, PRODUCTS_PATHS } from "@/features/products";

// ❌ INCORRECT - Import internal files directly
import { useProducts } from "@/features/products/hooks/useProducts";
import { ProductCard } from "@/features/products/components/ProductCard";

The index.ts acts as a facade or public contract:

// src/features/products/index.ts

// ✅ Expose data hooks
export { useProducts, useProduct } from "./hooks";

// ✅ Expose routes and paths
export { productsRouter } from "./router";
export { PRODUCTS_PATHS } from "./router/paths";

// ✅ Expose "widget" components designed to be shared
export { ProductWidget } from "./components/ProductWidget";

// ❌ DO NOT expose services
// export * from './services';

// ❌ DO NOT expose internal components
// export { ProductCard } from './components/ProductCard';

// ❌ DO NOT expose internal utils
// export * from './utils';

4. 📦 The Public API is Minimal and Intentional

By default, only export:

| Export | Example | Reason | | ---------------------- | ---------------- | --------------------------------------- | | ✅ Data hooks | useProducts | To consume feature data | | ✅ Router | productsRouter | To mount routes in app router | | ✅ Path constants | PRODUCTS_PATHS | For navigation from other parts | | ✅ Widget components | ProductWidget | Components explicitly designed to share | | ❌ Services | getProducts | Implementation details (private) | | ❌ Internal components | ProductCard | Only for internal feature use | | ❌ Utils | formatPrice | Private helpers |

5. 🔐 Implementation Details are Private

Internal components, service functions, and utilities should NOT be exported:

// ❌ NEVER do this
// src/features/products/index.ts
export { getProducts } from "./services/products.service";
export { ProductCard } from "./components/ProductCard";
export { formatPrice } from "./utils/product.utils";

// ✅ Instead, encapsulate them in hooks
// src/features/products/hooks/useProducts.ts
import { useQuery } from "@tanstack/react-query";
import { getProducts } from "../services/products.service";

export const useProducts = () => {
  return useQuery({
    queryKey: ["products"],
    queryFn: getProducts,
  });
};

6. 🌐 Shared Code is for Generic Logic

src/shared/ contains truly reusable code that is NOT tied to any business logic:

// ✅ CORRECT - Generic utilities in shared
// src/shared/utils/format.ts
export const formatCurrency = (amount: number) => {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
  }).format(amount);
};

// ❌ INCORRECT - Business logic in shared
// src/shared/utils/product.ts
export const calculateProductDiscount = (product: Product) => {
  // This logic is product-specific, shouldn't be in shared
};

7. ➡️ Dependencies are Primarily Unidirectional

┌─────────────┐
│   Feature   │ ←── Can import
└──────┬──────┘
       │
       ↓
┌─────────────┐
│   Shared    │ ←── Should NOT import feature details
└──────┬──────┘
       │
       ↓
┌─────────────┐
│     Lib     │ ←── Library configuration
└─────────────┘

General rule:

  • ✅ Features can import from @/shared
  • ✅ Shared can import from @/lib
  • ❌ Shared should NOT import feature internal details
  • Exception: Shared can use the Public API of features

Controlled exception:

High-level structural components in @/shared (like MainLayout) CAN consume hooks exposed in feature Public APIs:

// ✅ ALLOWED - Use feature Public API in shared
// src/shared/components/Layout/MainLayout.tsx
import { useAuth } from "@/features/auth"; // Public API

export const MainLayout = () => {
  const { user, logout } = useAuth();
  // ...
};

Recommended Data Flow

┌──────────┐
│   Page   │  Page component
└────┬─────┘
     │ uses
     ↓
┌──────────┐
│   Hook   │  Custom hook (orchestration)
└────┬─────┘
     │ calls
     ↓
┌──────────┐
│ Service  │  Async function that fetches
└────┬─────┘
     │ uses
     ↓
┌──────────┐
│  Axios   │  Configured HTTP client
└──────────┘

Advantages of This Architecture

  1. Scalability: Add features without breaking others
  2. Clarity: Structure reveals business purpose
  3. Maintainability: Everything related is together
  4. Testability: Features can be tested in isolation
  5. Parallel Work: Teams work on features without conflicts
  6. Onboarding: New developers understand structure quickly

5. Testing

BuildFast comes with Vitest and Testing Library preconfigured for a modern and fast testing experience.

Configuration

Testing is configured in vite.config.js:

export default defineConfig({
  // ...
  test: {
    globals: true,
    setupFiles: "./src/lib/test/setup.ts",
    environment: "jsdom",
    coverage: {
      reporter: ["text", "json", "html"],
    },
  },
});

Run Tests

# Run all tests
npm run test

# Run in watch mode
npm run test:watch

# Run with coverage
npm run test:coverage

# Run tests for a specific feature
npm run test -- features/products

Test Structure

Tests should be placed near the code they test:

src/features/products/
├── components/
│   ├── ProductCard.tsx
│   └── ProductCard.test.tsx     # Component test
├── hooks/
│   ├── useProducts.ts
│   └── useProducts.test.ts      # Hook test
└── utils/
    ├── product.utils.ts
    └── product.utils.test.ts    # Utility test

Example: Component Test

// src/features/products/components/ProductCard.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { ProductCard } from './ProductCard';

describe('ProductCard', () => {
  const mockProduct = {
    id: '1',
    name: 'Product Test',
    price: 99.99,
  };

  it('renders the product name', () => {
    render(<ProductCard product={mockProduct} />);
    expect(screen.getByText('Product Test')).toBeInTheDocument();
  });

  it('renders the formatted price', () => {
    render(<ProductCard product={mockProduct} />);
    expect(screen.getByText('$99.99')).toBeInTheDocument();
  });
});

Example: Hook Test with React Query

// src/features/products/hooks/useProducts.test.ts
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useProducts } from './useProducts';
import * as productsService from '../services/products.service';

// Mock the service
vi.mock('../services/products.service');

// Wrapper for React Query
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
    },
  });
  return ({ children }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('useProducts', () => {
  it('fetches products successfully', async () => {
    const mockProducts = [
      { id: '1', name: 'Product 1' },
      { id: '2', name: 'Product 2' },
    ];

    vi.mocked(productsService.getProducts).mockResolvedValue(mockProducts);

    const { result } = renderHook(() => useProducts(), {
      wrapper: createWrapper(),
    });

    expect(result.current.isLoading).toBe(true);

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data).toEqual(mockProducts);
  });
});

Example: Utility Test

// src/shared/utils/format.test.ts
import { describe, it, expect } from "vitest";
import { formatCurrency, formatDate } from "./format";

describe("formatCurrency", () => {
  it("formats positive numbers correctly", () => {
    expect(formatCurrency(1234.56)).toBe("$1,234.56");
  });

  it("formats zero correctly", () => {
    expect(formatCurrency(0)).toBe("$0.00");
  });
});

Testing Protected Routes

// src/features/auth/components/ProtectedRoute.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import { ProtectedRoute } from './ProtectedRoute';
import * as authHooks from '../hooks/useAuth';

vi.mock('../hooks/useAuth');

describe('ProtectedRoute', () => {
  it('renders children if user is authenticated', () => {
    vi.mocked(authHooks.useAuth).mockReturnValue({
      user: { id: '1', email: '[email protected]' },
      isLoading: false,
    });

    render(
      <MemoryRouter>
        <ProtectedRoute>
          <div>Protected Content</div>
        </ProtectedRoute>
      </MemoryRouter>
    );

    expect(screen.getByText('Protected Content')).toBeInTheDocument();
  });

  it('redirects if user is not authenticated', () => {
    vi.mocked(authHooks.useAuth).mockReturnValue({
      user: null,
      isLoading: false,
    });

    render(
      <MemoryRouter>
        <ProtectedRoute>
          <div>Protected Content</div>
        </ProtectedRoute>
      </MemoryRouter>
    );

    expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
  });
});

Testing Utilities

Create custom utilities in src/lib/test/utils.tsx:

// src/lib/test/utils.tsx
import { render } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';

export const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

export const renderWithProviders = (ui: React.ReactElement) => {
  const queryClient = createTestQueryClient();

  return render(
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>{ui}</BrowserRouter>
    </QueryClientProvider>
  );
};

Testing Best Practices

  1. Test behavior, not implementation: Focus on how the user interacts
  2. Use data-testid only when necessary: Prefer queries by text or role
  3. Mock external services: Don't make real API calls in tests
  4. Unit tests for complex logic: Hooks, utils, and pure functions
  5. Integration tests for critical flows: Login, checkout, etc.

6. Code Conventions

BuildFast implements strict conventions to maintain code quality and consistency.

Git Hooks (Husky)

Git hooks run automatically:

Pre-commit

Runs before each commit:

# .husky/pre-commit
npm run lint        # Check ESLint errors
npm run format      # Format with Prettier

Commit-msg

Validates commit message with Commitlint:

# .husky/commit-msg
npx commitlint --edit $1

Pre-push

Runs before push (optional):

# .husky/pre-push
npm run test        # Run all tests
npm run build       # Verify build works

Commit Format (Conventional Commits)

BuildFast uses Conventional Commits for standardized messages:

<type>(<scope>): <description>

[optional body]

[optional footer]

Allowed Types

| Type | Description | Example | | ---------- | ------------------------------------ | ----------------------------------------- | | feat | New feature | feat(products): add product filtering | | fix | Bug fix | fix(auth): resolve login redirect issue | | docs | Documentation changes | docs(readme): update installation steps | | style | Format changes (don't affect code) | style: format with prettier | | refactor | Refactoring (not bug fix or feature) | refactor(api): simplify error handling | | perf | Performance improvement | perf(images): lazy load product images | | test | Add or modify tests | test(hooks): add tests for useProducts | | chore | Build, deps changes, etc | chore: update dependencies | | ci | CI/CD changes | ci: add github actions workflow |

Commit Examples

# New feature
git commit -m "feat(products): add search functionality"

# Bug fix
git commit -m "fix(cart): prevent duplicate items"

# Refactoring
git commit -m "refactor(auth): extract validation logic to utils"

# Breaking change
git commit -m "feat(api)!: migrate to new API version

BREAKING CHANGE: API endpoints now use /v2/ prefix"

# With scope and detailed description
git commit -m "feat(checkout): add payment processing

- Integrate Stripe payment
- Add loading states
- Handle payment errors
- Add success confirmation"

ESLint

Configuration in eslint.config.js:

export default {
  extends: ["@typescript-eslint/recommended", "prettier"],
  rules: {
    "@typescript-eslint/no-unused-vars": "warn",
    "@typescript-eslint/no-explicit-any": "error",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
  },
};

Run ESLint

# Check errors
npm run lint

# Auto fix
npm run lint -- --fix

Prettier

Configuration in prettier.config.cjs:

module.exports = {
  semi: true,
  singleQuote: true,
  tabWidth: 2,
  trailingComma: "es5",
  printWidth: 80,
  arrowParens: "avoid",
};

Run Prettier

# Format all
npm run format

# Check without changing
npm run format -- --check

Naming Conventions

Files

PascalCase:   ComponentName.tsx, ProductCard.tsx
camelCase:    useProducts.ts, productService.ts
kebab-case:   product-list.css (if using CSS modules)

TypeScript Code

// ✅ Interfaces and Types - PascalCase
interface Product {
  id: string;
  name: string;
}

type ProductStatus = "active" | "inactive";

// ✅ Variables and functions - camelCase
const productName = "Product";
const getProduct = (id: string) => {};

// ✅ Global constants - SCREAMING_SNAKE_CASE
const API_BASE_URL = "https://api.example.com";
const MAX_RETRIES = 3;

// ✅ React components - PascalCase
const ProductCard = () => {};

// ✅ Custom Hooks - camelCase with "use" prefix
const useProducts = () => {};

// ✅ Service files - camelCase with ".service" suffix
// product.service.ts
export const getProducts = () => {};

// ✅ Route paths - SCREAMING_SNAKE_CASE in object
export const PRODUCTS_PATHS = {
  ROOT: "/products",
  DETAIL: "/products/:id",
};

Import Structure

Order imports this way:

// 1. React and external library imports
import { useState, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";

// 2. Feature imports (using public API)
import { useAuth } from "@/features/auth";

// 3. Shared imports
import { formatDate } from "@/shared/utils";
import { Button as CustomButton } from "@/shared/components";

// 4. Relative imports (only within same feature)
import { useProducts } from "../hooks/useProducts";
import { ProductCard } from "../components/ProductCard";

// 5. Type imports
import type { Product } from "../types";

// 6. Asset imports
import logo from "./logo.svg";

TypeScript Best Practices

// ✅ Use explicit types for props
interface ProductCardProps {
  product: Product;
  onSelect: (id: string) => void;
}

// ✅ Avoid "any" - use "unknown" if needed
const parseJSON = (json: string): unknown => {
  return JSON.parse(json);
};

// ✅ Use explicit return types
const getProductName = (product: Product): string => {
  return product.name;
};

// ✅ Use generics when appropriate
const createState = <T>(initialValue: T): [T, (value: T) => void] => {
  // ...
};

// ✅ Mark optional properties correctly
interface User {
  id: string;
  name: string;
  email?: string; // Optional
}

7. Environment Variables

BuildFast uses separate environment files per environment.

Environment Files

.env.development    # Variables for local development
.env.production     # Variables for production
.env.local         # Local variables (don't commit)

Required Variables

Stripe (Optional)

# For development, use test keys: https://dashboard.stripe.com/test/apikeys
VITE_APP_STRIPE_PUBLISHABLE_KEY=pk_test_...

Regional Configuration

# Locale for dates and formats
locale=enUS        # Options: enUS, esES, etc.

Access Environment Variables

// ✅ Access in code
const stripeKey = import.meta.env.VITE_APP_STRIPE_PUBLISHABLE_KEY;

// ✅ With default values
const apiUrl = import.meta.env.VITE_APP_API_URL || "http://localhost:3000";

// ⚠️ Variables must start with VITE_APP_ to be accessible
// ❌ BAD:  API_KEY=xxx
// ✅ GOOD: VITE_APP_API_KEY=xxx

Variable Validation

Create a file to validate required variables:

// src/lib/config/env.ts
const requiredEnvVars = [
  "VITE_APP_SUPABASE_URL",
  "VITE_APP_SUPABASE_ANON_KEY",
] as const;

export const validateEnv = () => {
  const missing = requiredEnvVars.filter((key) => !import.meta.env[key]);

  if (missing.length > 0) {
    throw new Error(
      `Missing required environment variables: ${missing.join(", ")}`,
    );
  }
};

// Call in main.tsx
import { validateEnv } from "@/lib/config/env";
validateEnv();

Best Practices

  1. Never commit .env.local: Add to .gitignore
  2. Document all variables: Keep this README updated
  3. Use variables for secrets: Don't hardcode API keys
  4. Validate at startup: Fail fast if critical variables are missing
  5. Different values per environment: Use .env.development and .env.production

8. Available Scripts

Development

# Start development server (port 5173)
npm run dev

# Start with specific port
npm run dev -- --port 3000

# Start with specific host (for external access)
npm run dev -- --host

Build

# Build for production
npm run build

# Preview production build
npm run preview

# Build with bundle analysis
npm run build -- --analyze

Testing

# Run all tests
npm run test

# Watch mode
npm run test:watch

# With coverage
npm run test:coverage

# Tests for specific feature
npm run test -- src/features/products

Linting & Formatting

# Check ESLint errors
npm run lint

# Auto fix ESLint
npm run lint -- --fix

# Format with Prettier
npm run format

# Check format without changing
npm run format -- --check

Git Hooks

# Reinstall Husky hooks
npm run prepare

# Run pre-commit hook manually
npm run pre-commit

# Run pre-push hook manually
npm run pre-push

9. Deployment

Build for Production

# 1. Make sure you have the correct environment variables
# Edit .env.production with your production credentials

# 2. Run the build
npm run build

# 3. Ready files will be in /dist

Deployment on Vercel

Option 1: From GitHub

  1. Push your code to GitHub
  2. Go to vercel.com and connect your repository
  3. Configure environment variables:
    • VITE_APP_SUPABASE_URL
    • VITE_APP_SUPABASE_ANON_KEY
    • VITE_APP_STRIPE_PUBLISHABLE_KEY (optional)
  4. Automatic deployment with each push to main

Option 2: Vercel CLI

# Install Vercel CLI
npm i -g vercel

# Login
vercel login

# Deploy
vercel

# Deploy to production
vercel --prod

Deployment on Netlify

From GitHub

  1. Connect your repository at netlify.com
  2. Configuration:
    • Build command: npm run build
    • Publish directory: dist
  3. Add environment variables in Site Settings > Environment

With CLI

# Install Netlify CLI
npm i -g netlify-cli

# Login
netlify login

# Deploy
netlify deploy

# Deploy to production
netlify deploy --prod

Deployment on GitHub Pages

# 1. Install gh-pages
npm install --save-dev gh-pages

# 2. Add scripts in package.json
{
  "scripts": {
    "predeploy": "npm run build",
    "deploy": "gh-pages -d dist"
  }
}

# 3. Configure base in vite.config.js
export default defineConfig({
  base: '/repo-name/',
  // ...
})

# 4. Deploy
npm run deploy

Environment Variables in Production

Make sure to configure these variables on your platform:

VITE_APP_STRIPE_PUBLISHABLE_KEY=pk_live_...  # Production key

Deployment Checklist

  • [ ] Environment variables configured correctly
  • [ ] Build runs without errors (npm run build)
  • [ ] Tests pass (npm run test)
  • [ ] Linting passes (npm run lint)
  • [ ] Stripe URLs are production ones
  • [ ] Sitemap generated (if applicable)
  • [ ] Analytics configured (Google Analytics, Mixpanel, etc.)
  • [ ] SEO optimized (meta tags, Open Graph)
  • [ ] Performance verified (Lighthouse)

10. Best Practices

Architecture

  1. Follow Screaming Architecture: Organize by features, not technical types
  2. Respect the Public API: Only import from index.ts of features
  3. Avoid circular dependencies: Features shouldn't import from each other
  4. Keep features small: If a feature grows too large, split it

React & Hooks

  1. Use TypeScript: Always type props, states, and returns
  2. Small components: One responsibility per component
  3. Custom hooks for shared logic: Reuse logic between components
  4. Memoization when necessary: useMemo, useCallback to optimize
// ✅ Typed component
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
}

export const Button: React.FC<ButtonProps> = ({
  label,
  onClick,
  variant = 'primary',
}) => {
  return <button onClick={onClick}>{label}</button>;
};

State Management

  1. React Query for server state: API data
  2. useState for UI state: Simple local states
  3. Context for limited global state: Theme, Auth
  4. URL as source of truth: Filters, pages, searches
// ✅ Server state with React Query
const { data: products } = useQuery({
  queryKey: ["products", filters],
  queryFn: () => getProducts(filters),
});

// ✅ Local UI state
const [isOpen, setIsOpen] = useState(false);

// ✅ State in URL
const [searchParams] = useSearchParams();
const page = searchParams.get("page") || "1";

Performance

  1. Lazy load routes: Use React.lazy for code splitting
  2. Lazy load images: Use loading="lazy" in <img> tags
  3. Virtualize long lists: Use react-window or similar
  4. Optimize bundle: Analyze with npm run build -- --analyze
// ✅ Lazy loading routes
const ProductsPage = lazy(() => import('./pages/ProductsPage'));

<Suspense fallback={<Loading />}>
  <ProductsPage />
</Suspense>;

Security

  1. Never expose secrets in frontend: Use environment variables
  2. Validate inputs: Use Yup or Zod for validation
  3. Sanitize HTML: Use DOMPurify if rendering dynamic HTML
  4. Use HTTPS in production: Always
  5. Configure CORS correctly: On your backend

API Calls

  1. Centralize Axios configuration: In src/lib/axios
  2. Use interceptors: For auth tokens, error handling
  3. Handle errors globally: With React Query or interceptors
  4. Implement retry logic: For critical calls
// ✅ Centralized Axios configuration
// src/lib/axios/client.ts
import axios from "axios";

export const apiClient = axios.create({
  baseURL: import.meta.env.VITE_APP_API_URL,
  timeout: 10000,
});

// Interceptor for auth token
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Interceptor for errors
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Redirect to login
      window.location.href = "/login";
    }
    return Promise.reject(error);
  },
);

Accessibility

  1. Use semantic HTML: Prefer native controls
  2. Add labels to inputs: Always
  3. Use landmarks: <button>, <nav>, <main>, etc.
  4. Test with keyboard: Navigation should work without mouse
  5. Adequate contrast: Use tools like Lighthouse

SEO

  1. Appropriate meta tags: Title, description, Open Graph
  2. Sitemap: Generate automatically
  3. Robots.txt: Configure correctly
  4. Semantic URLs: /products/gaming-laptop not /products?id=123

11. Troubleshooting

Common Issues

1. Error: "Cannot find module '@/features/...'"

Cause: Path aliases not configured correctly

Solution:

// Check vite.config.js
resolve: {
  alias: {
    '@': path.resolve(__dirname, './src'),
  },
}

// Check tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

2. Tests fail with "Cannot find module"

Cause: Vitest doesn't recognize path aliases

Solution:

// vite.config.js
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  // ...
});

3. Error: "Hooks can only be called inside the body of a function component"

Cause: Calling hooks outside components or custom hooks

Solution:

// ❌ BAD
const data = useQuery(...); // Outside component

export const Component = () => {
  // ...
}

// ✅ GOOD
export const Component = () => {
  const data = useQuery(...); // Inside component
  // ...
}

4. Build fails with "out of memory"

Cause: Bundle too large or insufficient memory

Solution:

# Increase Node memory
NODE_OPTIONS=--max_old_space_size=4096 npm run build

# Or in package.json
{
  "scripts": {
    "build": "NODE_OPTIONS=--max_old_space_size=4096 vite build"
  }
}

5. Environment variables not read

Cause: Variables don't start with VITE_APP_

Solution:

# ❌ BAD
SUPABASE_URL=xxx

# ✅ GOOD
VITE_APP_SUPABASE_URL=xxx

6. "Cannot read property of undefined" in tests

Cause: Missing wrapper with providers in tests

Solution:

// Use custom render with providers
import { renderWithProviders } from '@/lib/test/utils';

renderWithProviders(<Component />);

7. Husky hooks don't execute

Cause: Hooks not installed correctly

Solution:

# Reinstall hooks
npm run prepare

# Check permissions (on Unix)
chmod +x .husky/*

Get Help

  1. Search in Issues: GitHub Issues
  2. Create an Issue: If you can't find your problem
  3. Check documentation: Of specific libraries
  4. Discord/Slack: If the project has a community

Useful Resources


Developed with ❤️ to accelerate your frontend development.

BuildFast - From idea to production in record time.