Skip to content

Plakumat/books-showcase

Repository files navigation

Books Showcase — Next.js App Router

This project is a frontend challenge built with Next.js App Router, focusing on modern React patterns such as Server Components, Route Handlers, runtime validation, SEO, and progressive client interactivity.

The goal is not only to make the application work, but to demonstrate clean architecture, explicit trade-offs, and production-ready decisions.

Live Demo

Tech Stack

  • Node.js 22.13.1
  • Next.js 16 (App Router)
  • React 19
  • TypeScript
  • Mantine UI
  • Zod (runtime schema validation)
  • Zustand (lightweight client-side state management)
  • Jest + Testing Library (unit tests)
  • Playwright (end-to-end tests)
  • Husky + Commitlint (commit discipline)
  • Vercel (deployment)

Project Structure (High Level)

src/
├── app/
│   ├── books/            # Server-rendered pages
│   ├── favorites/        # Server page + client island
|   ├── search/           # Client-driven search & filtering UI
│   ├── api/              # Route Handlers (mock API)
│   ├── sitemap.ts
│   ├── robots.ts
│   └── middleware.ts
│
├── components/
│   ├── book/             # BookCard, BookGrid
│   ├── favorites/        # Client-only interactive components
│   ├── search/
│   ├── ui/
│   └── common/
│
├── lib/
│   ├── books/            # Repository layer + Zod schemas
│   ├── seo/              # SEO helpers (canonical URLs, JSON-LD builders, metadata utilities)
│   └── data.ts           # Mock dataset (single source of truth)
│
├── store/
│   └── favoritesStore.ts # Zustand client store
│
├── styles/
│   └── theme.ts          # Design tokens and global styling setup (Mantine theme, CSS variables)
│
└── tests/
    ├── unit/
    └── e2e/

Architectural Decisions

Server Components vs Client Components

Server Components are the default. Client Components are used only when browser-only APIs or user interaction are required.

Server Components are responsible for:

  • Data fetching
  • SEO metadata generation
  • Static rendering and revalidation
  • Error and not-found boundaries

Client Components are responsible for:

  • User interaction (search, debounce)
  • Client-side state (favorites)
  • Event handlers and transitions

This follows the App Router mental model:

Server = data & structure
Client = interaction & state

Data Layer & Repository Pattern

The application uses a local in-memory dataset (BOOKS) as the single source of truth.

BOOKS → repository → Server Components
BOOKS → Route Handlers (/api/books)

Why not fetch /api/books internally?

Self-fetching during build or prerender (especially on Vercel) is fragile and often leads to runtime failures.

Chosen trade-off:

  • Server Components read directly from the repository
  • Route Handlers expose the same data as a mock API

This avoids build-time issues while keeping the API contract intact.

Runtime Validation with Zod

All data exposed by the repository and API routes is validated using Zod.

Benefits:

  • Guarantees runtime correctness
  • Acts as a contract between layers
  • Prevents silent data shape mismatches

Caching & Revalidation

Because the dataset is static and local:

  • Page-level revalidation is used (export const revalidate = 60)
  • cache() is used inside the repository to deduplicate calls within the same render cycle

This satisfies the caching and revalidation requirement without relying on internal HTTP fetches.

Favorites Feature (Zustand)

Favorites are implemented as a pure client-side concern using Zustand with persistence.

Key points:

  • Stored in localStorage
  • No server dependency
  • Minimal and intentional state management

Architecture:

  • Server pages fetch the full book list
  • Client components filter books using favorite IDs stored in Zustand

This cleanly separates server data from user preference state.

Edge Middleware (Cross-Cutting Concern)

A lightweight middleware.ts demonstrates a cross-cutting concern:

  • Reads the Accept-Language header
  • Derives a default locale (tr / en)
  • Applies it consistently before rendering

This showcases Edge Runtime usage without unnecessary complexity.

SEO

SEO is handled entirely on the server using the Next.js Metadata API:

  • Dynamic titles and descriptions
  • Open Graph and Twitter Card metadata
  • Canonical URLs
  • Structured data (JSON-LD) on detail pages
  • sitemap.ts and robots.ts

This ensures correctness, performance, and crawler compatibility.

Styling & UI

  • Mantine is used as the UI library
  • Custom styles use CSS Modules
  • BEM naming is applied where appropriate
  • Focus on consistent spacing, typography, and empty states

Testing

Unit / Component Tests

  • Jest + React Testing Library
  • Covers core UI components and states
  • Tests behavior rather than implementation details

Run:

yarn test

End-to-End (E2E) Tests

  • Playwright
  • Covers a critical user flow (list → detail)

Run:

yarn e2e

Commit Discipline

This project enforces Conventional Commits using Husky and Commitlint.

Required format:

type(scope): message

Examples:

feat(favorites): add favorites feature
fix(search): handle empty query
refactor(ui): extract favorite button

Invalid commit messages are blocked locally before commit.

Running the Project Locally

yarn install
yarn dev

Open http://localhost:3000

Deployment

The project is deployed on Vercel.

  • Production deployments are triggered from the main branch
  • CI ensures code quality before merge
  • No manual deployment steps are required

Final Notes

This project intentionally avoids over-engineering and focuses on:

  • Clear architectural boundaries
  • Explicit and documented trade-offs
  • App Router best practices
  • Maintainability over clever abstractions

The resulting structure mirrors how a production-grade Next.js App Router application can be built using mock data while remaining robust, testable, and deploy-safe.

About

Showcase app for books with Next.js App Router & Mantine

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors