Project Structure - Tectonica-Campaigns-Solutions/Tectonica-Dev-Team-Standards-Practices GitHub Wiki

Project Structure Standards

Overview

This guide defines our standard project structure for Next.js applications integrated with DatoCMS. Following these standards ensures consistency across projects, makes codebases predictable for team members, and facilitates maintenance and scalability.

Table of Contents

Directory Structure

Root Level Structure

project-root/
├── .github/                 # GitHub specific files
│   ├── workflows/          # CI/CD workflows
│   └── PULL_REQUEST_TEMPLATE.md
├── .husky/                 # Git hooks
├── public/                 # Static files
│   ├── fonts/             # Web fonts
│   ├── images/            # Static images
│   └── locales/           # Translation files
├── scripts/                # Build/utility scripts
├── src/                    # Source code
├── tests/                  # E2E tests
├── .env.example           # Environment variables template
├── .eslintrc.json         # ESLint configuration
├── .gitignore             # Git ignore rules
├── .nvmrc                 # Node version
├── .prettierrc            # Prettier configuration
├── README.md              # Project documentation
├── next.config.js         # Next.js configuration
├── package.json           # Dependencies and scripts
├── tailwind.config.ts     # Tailwind configuration
└── tsconfig.json          # TypeScript configuration

Source Directory Structure

src/
├── app/                    # Next.js App Router
│   ├── (auth)/            # Route groups
│   ├── api/               # API routes
│   ├── [locale]/          # Internationalization
│   ├── error.tsx          # Error boundary
│   ├── layout.tsx         # Root layout
│   ├── loading.tsx        # Loading state
│   └── page.tsx           # Home page
├── components/             # React components
│   ├── common/            # Shared components
│   ├── forms/             # Form components
│   ├── layout/            # Layout components
│   ├── sections/          # Page sections
│   └── ui/                # UI primitives
├── hooks/                  # Custom React hooks
├── lib/                    # Core libraries
│   ├── datocms/           # DatoCMS integration
│   ├── utils/             # Utility functions
│   └── constants/         # App constants
├── styles/                 # Global styles
├── types/                  # TypeScript types
│   ├── generated/         # Auto-generated types
│   └── index.ts           # Manual type definitions
└── middleware.ts          # Next.js middleware

File Naming Conventions

General Rules

  1. Use kebab-case for files and folders

    ✅ user-profile.tsx
    ✅ api-helpers.ts
    ❌ userProfile.tsx
    ❌ ApiHelpers.ts
    
  2. Use PascalCase for component files

    ✅ UserProfile.tsx
    ✅ NavigationMenu.tsx
    ❌ user-profile.tsx
    ❌ navigationMenu.tsx
    
  3. Use index files for cleaner imports

    components/
    └── Button/
        ├── Button.tsx      # Component implementation
        ├── Button.test.tsx # Component tests
        ├── Button.module.css # Component styles
        └── index.ts        # Re-export
    

Specific Conventions

Components: PascalCase.tsx
  - Button.tsx
  - UserCard.tsx
  - NavigationHeader.tsx

Hooks: camelCase with 'use' prefix
  - useAuth.ts
  - useLocalStorage.ts
  - useDatoSubscription.ts

Utilities: camelCase.ts
  - formatDate.ts
  - parseUrl.ts
  - validateEmail.ts

Types: PascalCase.ts or .d.ts
  - User.ts
  - ApiResponse.ts
  - dato.d.ts

Constants: UPPER_SNAKE_CASE or camelCase
  - API_ENDPOINTS.ts
  - colorPalette.ts
  - CONFIG.ts

Tests: [name].test.ts(x) or [name].spec.ts(x)
  - Button.test.tsx
  - formatDate.test.ts
  - api.spec.ts

Component Organization

Component Structure

Each component should follow this structure:

components/
└── ComponentName/
    ├── ComponentName.tsx       # Main component
    ├── ComponentName.test.tsx  # Unit tests
    ├── ComponentName.stories.tsx # Storybook stories
    ├── ComponentName.module.css # CSS modules (if needed)
    ├── index.ts               # Public API
    └── types.ts               # Component-specific types

Component Categories

Common Components (/components/common/)

Reusable across the entire application:

common/
├── Button/
├── Card/
├── Modal/
├── Spinner/
└── Typography/

Layout Components (/components/layout/)

Page structure and navigation:

layout/
├── Header/
├── Footer/
├── Sidebar/
├── Navigation/
└── PageContainer/

Form Components (/components/forms/)

Form inputs and validation:

forms/
├── Input/
├── Select/
├── Checkbox/
├── RadioGroup/
└── FormField/

Section Components (/components/sections/)

Page-specific sections:

sections/
├── Hero/
├── Features/
├── Testimonials/
├── CallToAction/
└── Newsletter/

UI Components (/components/ui/)

Basic UI elements:

ui/
├── Badge/
├── Avatar/
├── Tooltip/
├── Dropdown/
└── Tabs/

Component File Template

// components/Button/Button.tsx
import { FC, ButtonHTMLAttributes } from 'react';
import clsx from 'clsx';
import styles from './Button.module.css';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  isLoading?: boolean;
}

export const Button: FC<ButtonProps> = ({
  children,
  variant = 'primary',
  size = 'md',
  isLoading = false,
  className,
  disabled,
  ...props
}) => {
  return (
    <button
      className={clsx(
        styles.button,
        styles[variant],
        styles[size],
        {
          [styles.loading]: isLoading,
        },
        className
      )}
      disabled={disabled || isLoading}
      {...props}
    >
      {isLoading ? <Spinner /> : children}
    </button>
  );
};

// components/Button/index.ts
export { Button } from './Button';
export type { ButtonProps } from './Button';

Routing Structure

App Router Organization

app/
├── (marketing)/            # Marketing pages group
│   ├── about/
│   ├── contact/
│   └── pricing/
├── (app)/                  # Application pages group
│   ├── dashboard/
│   ├── settings/
│   └── profile/
├── auth/                   # Authentication pages
│   ├── login/
│   ├── register/
│   └── forgot-password/
├── api/                    # API routes
│   ├── auth/
│   ├── webhooks/
│   └── revalidate/
└── [locale]/              # Internationalization
    ├── layout.tsx
    └── page.tsx

Route File Structure

Each route should contain:

route-name/
├── page.tsx               # Page component
├── layout.tsx             # Layout (if needed)
├── loading.tsx            # Loading state
├── error.tsx              # Error boundary
├── route.ts               # API route (in api/ folder)
└── opengraph-image.tsx    # OG image generation

Dynamic Routes

app/
├── blog/
│   ├── [slug]/
│   │   ├── page.tsx
│   │   └── loading.tsx
│   └── page.tsx
└── products/
    ├── [category]/
    │   └── [id]/
    │       └── page.tsx
    └── page.tsx

API and Data Layer

DatoCMS Integration Structure

lib/
└── datocms/
    ├── client.ts          # GraphQL client setup
    ├── queries/           # GraphQL queries
    │   ├── pages.ts
    │   ├── posts.ts
    │   ├── products.ts
    │   └── fragments.ts
    ├── types/             # Generated types
    │   └── dato.ts
    └── utils/             # DatoCMS utilities
        ├── imageLoader.ts
        └── performRequest.ts

Query Organization

// lib/datocms/queries/posts.ts
import { gql } from 'graphql-request';
import { IMAGE_FRAGMENT, SEO_FRAGMENT } from './fragments';

export const ALL_POSTS_QUERY = gql`
  query AllPosts($first: IntType, $skip: IntType) {
    allPosts(first: $first, skip: $skip, orderBy: _publishedAt_DESC) {
      id
      title
      slug
      excerpt
      publishedAt
      coverImage {
        ...ImageFragment
      }
      author {
        name
        picture {
          ...ImageFragment
        }
      }
    }
  }
  ${IMAGE_FRAGMENT}
`;

export const POST_BY_SLUG_QUERY = gql`
  query PostBySlug($slug: String!) {
    post(filter: { slug: { eq: $slug } }) {
      id
      title
      slug
      content
      publishedAt
      seo {
        ...SeoFragment
      }
      coverImage {
        ...ImageFragment
      }
      author {
        name
        bio
        picture {
          ...ImageFragment
        }
      }
    }
  }
  ${IMAGE_FRAGMENT}
  ${SEO_FRAGMENT}
`;

API Routes Structure

app/api/
├── auth/
│   ├── login/
│   │   └── route.ts
│   ├── logout/
│   │   └── route.ts
│   └── refresh/
│       └── route.ts
├── webhooks/
│   ├── dato/
│   │   └── route.ts
│   └── stripe/
│       └── route.ts
└── revalidate/
    └── route.ts

Configuration Files

Root Configuration Files

.env.example:
  - Template for environment variables
  - Document all required variables
  - Include example values

next.config.js:
  - Next.js configuration
  - Image domains
  - Redirects and rewrites
  - Environment variables

tsconfig.json:
  - TypeScript configuration
  - Path aliases
  - Compiler options

.eslintrc.json:
  - ESLint rules
  - Custom configurations
  - Plugin settings

.prettierrc:
  - Code formatting rules
  - Consistent styling

Environment Variables Organization

# .env.example

# Application
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_SITE_NAME="Your Site Name"

# DatoCMS
NEXT_PUBLIC_DATOCMS_API_TOKEN=
DATOCMS_API_TOKEN=
DATOCMS_PREVIEW_SECRET=
NEXT_PUBLIC_DATOCMS_ENVIRONMENT=main

# Authentication
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=

# External Services
STRIPE_PUBLIC_KEY=
STRIPE_SECRET_KEY=
SENDGRID_API_KEY=

# Analytics
NEXT_PUBLIC_GA_ID=
NEXT_PUBLIC_GTM_ID=

# Feature Flags
NEXT_PUBLIC_ENABLE_ANALYTICS=true
NEXT_PUBLIC_ENABLE_PWA=false

Asset Management

Public Assets

public/
├── fonts/                 # Self-hosted fonts
│   ├── inter-var.woff2
│   └── fonts.css
├── images/               # Static images
│   ├── logo.svg
│   ├── og-image.png
│   └── icons/
│       ├── favicon.ico
│       └── apple-touch-icon.png
├── locales/              # Translation files
│   ├── en/
│   │   └── common.json
│   └── es/
│       └── common.json
└── manifest.json         # PWA manifest

Image Component Usage

// Use Next.js Image for optimization
import Image from 'next/image';

// For DatoCMS images
import { Image as DatoImage } from 'react-datocms';

// For static images
import logoImg from '@/public/images/logo.png';

SVG Management

components/
└── icons/
    ├── ArrowIcon.tsx
    ├── CloseIcon.tsx
    └── index.ts       # Export all icons

Testing Structure

Test Organization

tests/
├── e2e/                  # End-to-end tests
│   ├── auth.spec.ts
│   ├── navigation.spec.ts
│   └── checkout.spec.ts
├── integration/          # Integration tests
│   ├── api/
│   └── datocms/
└── fixtures/             # Test data
    ├── users.json
    └── products.json

# Unit tests co-located with components
components/Button/Button.test.tsx
lib/utils/formatDate.test.ts

Test File Naming

Unit Tests: [name].test.ts(x)
Integration Tests: [name].integration.test.ts
E2E Tests: [name].spec.ts
Test Utilities: [name].test-utils.ts
Mock Data: [name].mock.ts

Documentation

Documentation Structure

docs/
├── API.md                # API documentation
├── DEPLOYMENT.md         # Deployment guide
├── DEVELOPMENT.md        # Development guide
├── ARCHITECTURE.md       # Architecture decisions
└── CONTRIBUTING.md       # Contribution guidelines

# Component documentation
components/Button/README.md

# Feature documentation
app/features/checkout/README.md

README Template

# Project Name

## Overview
Brief description of the project

## Tech Stack
- Next.js 14
- DatoCMS
- TypeScript

## Getting Started
1. Clone the repository
2. Install dependencies
3. Set up environment variables
4. Run development server

## Project Structure
Overview of folder organization

## Development
- Code standards
- Git workflow
- Testing approach

## Deployment
- Build process
- Environment management
- CI/CD pipeline

## Contributing
Link to contributing guidelines

Best Practices Checklist

Folder Structure

  • Consistent naming conventions
  • Logical component grouping
  • Clear separation of concerns
  • Co-located tests and styles
  • Proper use of index files

File Organization

  • One component per file
  • Related files grouped together
  • Clear import/export structure
  • Proper TypeScript types location
  • Documentation where needed

Code Organization

  • DRY principle followed
  • Reusable components extracted
  • Business logic separated
  • Utilities properly categorized
  • Constants centralized

Routing

  • RESTful URL structure
  • Proper use of route groups
  • Loading and error states
  • SEO-friendly URLs
  • Consistent naming

Migration Guide

When restructuring existing projects:

  1. Plan the migration

    • Document current structure
    • Map to new structure
    • Identify breaking changes
  2. Gradual migration

    • Start with new features
    • Refactor module by module
    • Update imports progressively
  3. Update tooling

    • Update path aliases
    • Fix build scripts
    • Update test configurations
  4. Team communication

    • Document changes
    • Update team guides
    • Conduct training session

Related Pages

Last Updated

June 9, 2025 - Comprehensive structure guide created

⚠️ **GitHub.com Fallback** ⚠️