Work in Progress

Next.js

Next.js App Router (TypeScript) Style Guide

General Principles


Folder and File Structure

app/        # App Router pages
components/ # Reusable components
hooks/      # Custom hooks
lib/        # Utilities and helpers
services/   # API calls or external integrations
styles/     # Global and modular styles
types/      # TypeScript types and interfaces
context/    # Context API setup
middleware/ # Middleware (if needed)
config/     # Configuration files
layout.tsx
page.tsx
global.css
middleware.ts
config.ts
package.json

File Naming

  • Use kebab-case for React component files, utility, hooks, or helper files.

Examples:

app/home/page.tsx
components/ui/button.tsx
components/login-form.tsx
hooks/use-fetch.ts
lib/format-date.ts

Imports

  • Organize imports from libraries to internal files:
// React or third-party libraries
import { useState } from 'react';
import { format } from 'date-fns';

// Internal components and helpers
import NavBar from '@/components/navbar';
import { formatDate } from '@/lib/format-date';

React Components

  • Use functional components
  • Use TypeScript for typing props and state.

Example:

interface LoginProps {
status?: string;
canResetPassword: boolean;

}

export default fanction LoginForm({status, canResetPassword}: LoginProps){

return (
  <div>LoginForm</div>

 )
}

TypeScript

  • Enable strict mode in tsconfig.json:
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
  • Avoid using any. Always use specific types.

Example:

// Correct
const fetchUser = async (id: number): Promise<User> => {
  // ...
};

// Incorrect
const fetchUser = async (id: number): Promise<any> => {
  // ...
};

Styling

  • Use CSS Modules or Tailwind CSS for modular styling.

Example:

import styles from './NavBar.module.css';

const NavBar: React.FC = () => {
  return <nav className={styles.nav}>NavBar</nav>;
};

export default NavBar;
  • Global styles are stored in globals.css and imported in app/layout.tsx.
import './globals.css';

export const metadata = {
  title: 'Next.js App',
  description: 'Generated by Next.js',
};

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

Testing

  • Use Jest and React Testing Library for unit testing.

Example:

import { render, screen } from '@testing-library/react';
import Button from '@/components/Button';

test('renders Button with label', () => {
  render(<Button label="Click Me" onClick={() => {}} />);
  expect(screen.getByText(/Click Me/i)).toBeInTheDocument();
});
  • Use Playwright or Cypress for end-to-end testing.

Linting and Formatting

  • Use ESLint with the following configuration in eslint.config.mjs:
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const eslintConfig = [
  ...compat.extends("next/core-web-vitals", "next/typescript"),
];

export default eslintConfig;
  • Use Prettier with the following configuration in .prettierrc optional:
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80
}