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:

Rule Level Description
useSimplifiedLogicExpression error Simplify complex boolean logic
noNestedComponentDefinitions error Don’t define components inside components
noReactPropAssignments error Don’t assign to props directly
noCommonJs error Use ES modules only
noMagicNumbers error No literal numbers in code (use constants)
useBlockStatements error Always use braces for conditionals
noEnum error Use type unions instead of enums
noParameterAssign error Don’t reassign function parameters
useConsistentArrayType error Use string[] not Array<string>
useReactFunctionComponent error Use function components only
noConsole warn Avoid console.log in production
noAlert error Never 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 (!isPayloadMedia(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 { getPayload } from '@/lib/payload/local-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)