Skip to content

JavaScript, React & Next.js Style Guide

This guide covers JavaScript, React, and Next.js conventions enforced by Biome and project-specific patterns.

  • 4 spaces for indentation (no tabs)
  • Single quotes for JavaScript strings ('hello')
  • Double quotes for JSX attributes (<div className="card">)
  • Trailing commas on multiline objects and arrays
const items = ['first', 'second', 'third'];
  • Maximum 120 characters per line
  • Break long lines at logical points (after commas, before operators)
  • Use interface for object shapes, type for unions/aliases
  • Use type for primitive aliases and function types
  • Export types separately when needed
interface Props {
title: string;
items: string[];
}
type Status = 'loading' | 'success' | 'error';
  • Define component props using interface Props
  • Use extends to extend other prop types
  • Use Omit to exclude properties
interface Props extends React.HTMLAttributes<HTMLDivElement> {
title: string;
variant?: 'primary' | 'secondary';
}
  • Use function declarations for top-level exports
  • Use arrow functions for inline or nested components
  • Always use explicit return types when props are complex
interface Props {
title: string;
}
export default function MyComponent({ title }: Props) {
return <h1>{title}</h1>;
}
  1. Imports (external, then internal)
  2. Types/interfaces
  3. Constants
  4. Component function
  5. Return statement
import Image from 'next/image';
import type { Media } from '@/payload-types';
interface Props {
src: Media;
}
const DEFAULT_WIDTH = 800;
export default function MyImage({ src }: Props) {
return <Image src={src.url} alt={src.alt} width={DEFAULT_WIDTH} />;
}
  • Components: PascalCase (SiteHeader, PrimaryNavigation)
  • Files: kebab-case (site-header.tsx, primary-navigation.tsx)
  • Constants: SCREAMING_SNAKE
const DEFAULT_WIDTH = 800;
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;
  • Default to Server Components
  • Add 'use client' only when needed (hooks, browser APIs, event handlers)
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
  • Always use next/image for images
  • Use sizes="auto" for dynamic images
  • Provide fallback width/height
<Image
sizes="auto"
src={image.url}
alt={image.alt}
width={image.width || DEFAULT_WIDTH}
height={image.height || DEFAULT_HEIGHT}
/>
  • Use next/link for internal navigation
  • Combine with a tag or spread props
import Link from 'next/link';
export function MyLink({ href, children }: { href: string; children: React.ReactNode }) {
return <Link href={href}>{children}</Link>;
}

This project enforces these rules via Biome:

RuleLevelDescription
useSimplifiedLogicExpressionerrorSimplify complex boolean logic
noNestedComponentDefinitionserrorDon’t define components inside components
noReactPropAssignmentserrorDon’t assign to props directly
noCommonJserrorUse ES modules only
noMagicNumberserrorNo literal numbers in code (use constants)
useBlockStatementserrorAlways use braces for conditionals
noEnumerrorUse type unions instead of enums
noParameterAssignerrorDon’t reassign function parameters
useConsistentArrayTypeerrorUse string[] not Array<string>
useReactFunctionComponenterrorUse function components only
noConsolewarnAvoid console.log in production
noAlerterrorNever use alert()
if (!data) {
return null;
}
return <div>{data.content}</div>;
export default function Component({
title,
variant = 'primary',
...rest
}: Props) {
return <div {...rest}>{title}</div>;
}
if (!isPayloadImage(props.src)) {
return null;
}
const image = props.src;
// image is now guaranteed to exist
  1. Node built-ins (path, fs, etc.)
  2. External packages (next, react, @payload)
  3. Internal packages (@/, ~/components)
  4. Relative imports
import path from 'path';
import Image from 'next/image';
import type { Media } from '@/payload-types';
import PrimaryNavigation from '../primary-navigation/primary-navigation';
  • Use @/ for src/ root
  • Use relative imports for sibling components
import { getSiteHeaderGlobal } from '@/globals/api';
import PrimaryNavigation from '../primary-navigation/primary-navigation';
  • Use TypeScript types strictly
  • Keep components small and focused
  • Extract logic into custom hooks
  • Use constants for magic numbers
  • Handle null/undefined explicitly
  • Use any type
  • Define components inside other components
  • Use numeric indexes for array keys
  • Leave console.log in production code
  • Use CommonJS (require, module.exports)