Skip to main content

Overview

The OWASP Nest frontend is built with Next.js 16, React 19, and TypeScript, using the App Router for server-side rendering and modern React features.

Tech Stack

Next.js 16

React framework with App Router

TypeScript

Type-safe JavaScript

Tailwind CSS 4

Utility-first CSS

Apollo Client

GraphQL client

HeroUI

React component library

pnpm

Fast package manager

Project Structure

frontend/
├── __tests__/              # Test files
│   ├── a11y/              # Accessibility tests
│   ├── e2e/               # End-to-end tests
│   ├── unit/              # Unit tests
│   └── mockData/          # Test fixtures
├── public/                 # Static assets
├── src/
│   ├── app/               # Next.js 16 App Router
│   │   ├── about/        # About pages
│   │   ├── api/          # API routes
│   │   ├── auth/         # Authentication pages
│   │   ├── chapters/     # Chapter pages
│   │   ├── committees/   # Committee pages
│   │   ├── community/    # Community pages
│   │   ├── projects/     # Project pages
│   │   ├── layout.tsx    # Root layout
│   │   └── page.tsx      # Home page
│   ├── components/        # React components
│   │   ├── ui/           # Reusable UI components
│   │   ├── icons/        # Icon components
│   │   └── ...           # Feature components
│   ├── contexts/          # React contexts
│   ├── hooks/             # Custom React hooks
│   ├── server/            # Server-side utilities
│   ├── types/             # TypeScript types
│   ├── utils/             # Client utilities
│   └── wrappers/          # Component wrappers
├── .env                    # Environment variables
├── jest.config.ts          # Jest configuration
├── next.config.js          # Next.js configuration
├── package.json            # Dependencies
├── playwright.config.ts    # Playwright configuration
├── tailwind.config.js      # Tailwind configuration
└── tsconfig.json           # TypeScript configuration

Development Setup

Prerequisites

Ensure you have completed the local development setup first.

Dependencies

Dependencies are managed with pnpm and defined in package.json:
package.json
{
  "dependencies": {
    "next": "^16.1.6",
    "react": "^19.2.4",
    "react-dom": "^19.2.4",
    "typescript": "~5.9.3"
  }
}

Update Dependencies

make update-frontend-dependencies
This runs pnpm update to update all frontend dependencies.

Common Commands

# Runs on http://localhost:3000
pnpm run dev

Next.js App Router

File-Based Routing

Next.js uses file-based routing in the app/ directory:
app/
├── page.tsx               # / (home)
├── layout.tsx             # Root layout
├── about/
│   └── page.tsx          # /about
├── projects/
│   ├── page.tsx          # /projects
│   ├── [key]/
│   │   └── page.tsx      # /projects/[key]
│   └── dashboard/
│       └── page.tsx      # /projects/dashboard
└── api/
    └── auth/
        └── [...nextauth]/
            └── route.ts   # /api/auth/*

Layouts

app/layout.tsx
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        {children}
      </body>
    </html>
  )
}

Server vs Client Components

app/projects/page.tsx
// No "use client" directive = Server Component
import { getProjects } from '@/server/projects'

export default async function ProjectsPage() {
  const projects = await getProjects()
  
  return (
    <div>
      <h1>Projects</h1>
      {projects.map((project) => (
        <div key={project.id}>{project.name}</div>
      ))}
    </div>
  )
}
Benefits:
  • Zero client-side JavaScript
  • SEO-friendly
  • Direct database/API access

Data Fetching

GraphQL with Apollo Client

1

Define Query

app/projects/queries.graphql
query GetProjects($first: Int) {
  projects(first: $first) {
    edges {
      node {
        id
        name
        description
        url
      }
    }
  }
}
2

Generate Types

pnpm run graphql-codegen
Creates queries.generated.ts with TypeScript types.
3

Use in Component

app/projects/ProjectList.tsx
'use client'

import { useQuery } from '@apollo/client'
import { GetProjectsDocument } from './queries.generated'

export function ProjectList() {
  const { data, loading, error } = useQuery(GetProjectsDocument, {
    variables: { first: 10 },
  })
  
  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
  
  return (
    <div>
      {data?.projects?.edges?.map(({ node }) => (
        <div key={node.id}>{node.name}</div>
      ))}
    </div>
  )
}

REST API Fetch

app/projects/page.tsx
async function getProjects() {
  const res = await fetch('http://backend:8000/api/v0/projects/', {
    cache: 'no-store', // or 'force-cache'
  })
  return res.json()
}

export default async function ProjectsPage() {
  const { projects } = await getProjects()
  return <div>{/* Render projects */}</div>
}

Styling

Tailwind CSS

<div className="container mx-auto px-4">
  <h1 className="text-4xl font-bold text-gray-900 dark:text-white">
    OWASP Nest
  </h1>
  <p className="mt-4 text-lg text-gray-600 dark:text-gray-400">
    Your gateway to OWASP
  </p>
</div>

HeroUI Components

import { Button, Card, CardBody, CardHeader } from '@heroui/react'

export function ProjectCard({ project }) {
  return (
    <Card>
      <CardHeader>
        <h3>{project.name}</h3>
      </CardHeader>
      <CardBody>
        <p>{project.description}</p>
        <Button color="primary">View Project</Button>
      </CardBody>
    </Card>
  )
}

Authentication

NextAuth.js Setup

1

Configure Provider

app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth'
import GithubProvider from 'next-auth/providers/github'

const handler = NextAuth({
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      // Add custom session data
      return session
    },
  },
})

export { handler as GET, handler as POST }
2

Session Provider

app/layout.tsx
import { SessionProvider } from 'next-auth/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <SessionProvider>
          {children}
        </SessionProvider>
      </body>
    </html>
  )
}
3

Use in Components

components/UserMenu.tsx
'use client'

import { useSession, signIn, signOut } from 'next-auth/react'

export function UserMenu() {
  const { data: session } = useSession()
  
  if (!session) {
    return <button onClick={() => signIn('github')}>Sign In</button>
  }
  
  return (
    <div>
      <p>Welcome, {session.user?.name}</p>
      <button onClick={() => signOut()}>Sign Out</button>
    </div>
  )
}

Custom Hooks

hooks/useProjects.ts
import { useQuery } from '@apollo/client'
import { GetProjectsDocument } from '@/app/projects/queries.generated'

export function useProjects(limit = 10) {
  const { data, loading, error } = useQuery(GetProjectsDocument, {
    variables: { first: limit },
  })
  
  return {
    projects: data?.projects?.edges?.map(({ node }) => node) || [],
    loading,
    error,
  }
}
Usage:
const { projects, loading } = useProjects()

TypeScript

Type Definitions

types/project.ts
export interface Project {
  id: string
  name: string
  description: string
  level: 'Lab' | 'Production' | 'Flagship'
  type: 'Code' | 'Documentation' | 'Tool'
  url: string
  leaders: string[]
}

GraphQL Generated Types

Generated from GraphQL schema:
pnpm run graphql-codegen
Produces:
export type Project = {
  __typename?: 'ProjectNode'
  id: Scalars['ID']
  name: Scalars['String']
  description: Scalars['String']
  // ...
}

Testing

See the Testing Guide for comprehensive frontend testing documentation.
pnpm run test:unit

Environment Variables

Key variables in frontend/.env:
.env
# API URLs
NEXT_PUBLIC_API_URL=http://localhost:8000/
NEXT_PUBLIC_GRAPHQL_URL=http://localhost:8000/graphql/
NEXT_PUBLIC_CSRF_URL=http://localhost:8000/csrf/

# Environment
NEXT_PUBLIC_ENVIRONMENT=development
NEXT_PUBLIC_RELEASE_VERSION=1.0.0

# Auth
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=<secret>
GITHUB_ID=<github-oauth-id>
GITHUB_SECRET=<github-oauth-secret>

# Analytics (optional)
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX
NEXT_PUBLIC_SENTRY_DSN=<sentry-dsn>

# Search (optional)
NEXT_PUBLIC_IDX_URL=http://localhost:8000/idx/
Variables prefixed with NEXT_PUBLIC_ are exposed to the browser.

Performance Optimization

Image Optimization

import Image from 'next/image'

<Image
  src="/logo.png"
  alt="OWASP Nest"
  width={200}
  height={50}
  priority // Load immediately
/>

Code Splitting

import dynamic from 'next/dynamic'

const HeavyComponent = dynamic(() => import('@/components/HeavyComponent'), {
  loading: () => <div>Loading...</div>,
  ssr: false, // Client-side only
})

Memoization

import { memo, useMemo } from 'react'

export const ProjectCard = memo(({ project }) => {
  const formattedDate = useMemo(
    () => new Date(project.createdAt).toLocaleDateString(),
    [project.createdAt]
  )
  
  return <div>{formattedDate}</div>
})

Debugging

React DevTools

Install the React Developer Tools browser extension.

Next.js Debug Mode

NODE_OPTIONS='--inspect' pnpm run dev
Then attach debugger in Chrome DevTools or VS Code.

Console Logging

console.log('Debug:', data)
console.error('Error:', error)

Build & Deployment

Production Build

1

Build

pnpm run build
Creates optimized production build in .next/ directory.
2

Analyze Bundle

ANALYZE=true pnpm run build
Opens bundle analyzer to identify large dependencies.
3

Start Production Server

pnpm run start

Docker Build

make build-frontend-local-image
Builds production Docker image for deployment.

Next Steps

Testing Guide

Write and run frontend tests

Backend Development

Learn about Django backend

Components

Browse UI components

Contributing

Contribution guidelines

Build docs developers (and LLMs) love