Skip to content

ecelustka/react-todo

Repository files navigation

React Todo

A todo application built with React, Next.js, and TypeScript. Uses localStorage for persistence and shadcn/ui for the component library.

Tech Stack

  • Next.js 16 — App Router, server/client components
  • React 19useState + useEffect for localStorage sync
  • TypeScript 5 — strict type safety
  • Tailwind CSS 4 — utility-first styling
  • shadcn/ui — Radix UI primitives with Tailwind
  • React Hook Form + Zod — form handling and validation

Getting Started

npm install
npm run dev

Open http://localhost:3000.

Project Structure

src/
  app/
    layout.tsx          # Root layout, fonts, ThemeProvider
    page.tsx            # Home page, renders TodoList
    globals.css         # Tailwind config, CSS variables, keyframes
  components/
    todo-list.tsx       # Main orchestrator (state, filtering, progress)
    todo-item.tsx       # Single todo row (checkbox, text, actions)
    form-add-todo.tsx   # Add todo form with Zod validation
    card-empty-state.tsx        # Reusable empty state card
    dialog-confirm-delete.tsx   # Delete confirmation (AlertDialog)
    dialog-edit-todo.tsx        # Edit todo (Dialog)
    button-theme-toggle.tsx     # Dark/light mode toggle
    theme-provider.tsx          # next-themes wrapper
    ui/                 # shadcn/ui base components
  hooks/
    use-todos.ts        # Todo CRUD operations + localStorage sync
    use-mounted.ts      # Hydration-safe mounted check
  types/
    todo.ts             # Todo interface
  lib/
    utils.ts            # cn() helper (clsx + tailwind-merge)

Why suppressHydrationWarning?

The suppressHydrationWarning on the root <html> element and the todo list container are intentional and necessary. Here's why:

On <html> (layout.tsx)

next-themes adds a class="dark" or class="light" to <html> at runtime via a blocking script. During server-side rendering, React doesn't know what class the element will have — the server renders without it, and the client adds it immediately. This causes a harmless hydration mismatch on the class attribute. suppressHydrationWarning tells React to expect this and not warn about it. This is the recommended approach from both next-themes and shadcn/ui.

On the todo list container (todo-list.tsx)

Todos are stored in localStorage, which only exists in the browser. During SSR, useState initializes with an empty array. After hydration, a useEffect loads the real data from localStorage.

This means the server HTML and client HTML can differ (empty state vs. populated list). However, the entire container starts with opacity: 0 and only fades in after the useMounted() hook confirms hydration is complete. The user never sees the mismatch — it happens while the content is invisible. suppressHydrationWarning silences the expected warning for this specific element.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors