# iOS Reminders

> Smart lists, custom lists and checkable reminders.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/templates/ios-reminders
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/ios-reminders.json
```

Adds the page at `app/ios-reminders/page.tsx` together with the components below. Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Components

- [iOS Kit](https://chromotion.dev/r/ios-kit.md): `@/components/ios-kit`

## Dependencies

```bash
npm install lucide-react
```

## Source

```tsx title="app/ios-reminders/page.tsx"
"use client"

import * as React from "react"
import { Calendar, CalendarDays, Check, Flag, Inbox, ListChecks, Plus, ShoppingCart, Sun } from "lucide-react"

import { cn } from "@/lib/utils"
import { IOSBackButton, IOSDevice, IOSGroup, IOSNavButton, IOSPage, IOSRow } from "@/components/ios-kit"

type Item = { id: number; text: string; done: boolean }
type List = { id: string; name: string; color: string; text: string; items: Item[] }

const seed: List[] = [
  {
    id: "reminders", name: "Reminders", color: "bg-(color:--ios-orange)", text: "text-(color:--ios-orange)",
    items: [
      { id: 1, text: "Renew passport", done: false },
      { id: 2, text: "Call the dentist", done: true },
      { id: 3, text: "Water the plants", done: false },
    ],
  },
  {
    id: "groceries", name: "Groceries", color: "bg-(color:--ios-green)", text: "text-(color:--ios-green)",
    items: [
      { id: 1, text: "Oat milk", done: false },
      { id: 2, text: "Sourdough", done: false },
      { id: 3, text: "Lemons", done: true },
    ],
  },
  {
    id: "work", name: "Work", color: "bg-(color:--ios-blue)", text: "text-(color:--ios-blue)",
    items: [
      { id: 1, text: "Send launch checklist", done: false },
      { id: 2, text: "Review design tokens PR", done: false },
    ],
  },
]

const smart = [
  { label: "Today", icon: Sun, color: "bg-(color:--ios-blue)", count: 3 },
  { label: "Scheduled", icon: Calendar, color: "bg-(color:--ios-red)", count: 5 },
  { label: "All", icon: Inbox, color: "bg-zinc-600 dark:bg-zinc-500", count: 8 },
  { label: "Flagged", icon: Flag, color: "bg-(color:--ios-orange)", count: 1 },
]

const listIcons = { reminders: ListChecks, groceries: ShoppingCart, work: CalendarDays }

export default function IOSReminders() {
  const [lists, setLists] = React.useState(seed)
  const [openId, setOpenId] = React.useState<string | null>(null)
  const [draft, setDraft] = React.useState("")
  const open = lists.find((list) => list.id === openId)

  const update = (id: string, fn: (items: Item[]) => Item[]) =>
    setLists((all) => all.map((list) => (list.id === id ? { ...list, items: fn(list.items) } : list)))

  if (open) {
    const remaining = open.items.filter((item) => !item.done).length
    return (
      <IOSDevice>
        <IOSPage
          title={open.name}
          left={<IOSBackButton label="Lists" onClick={() => setOpenId(null)} />}
          right={<IOSNavButton aria-label="New reminder"><Plus className="size-6" /></IOSNavButton>}
          className="bg-(color:--ios-surface)"
        >
          <p className={cn("-mt-1 px-4 pb-3 text-[34px] leading-none font-bold tabular-nums", open.text)}>{remaining}</p>
          <ul className="px-4">
            {open.items.map((item) => (
              <li key={item.id} className="relative flex items-center gap-3 py-2.5 after:absolute after:right-0 after:bottom-0 after:left-9 after:h-px after:bg-(color:--ios-sep)">
                <button
                  type="button"
                  role="checkbox"
                  aria-checked={item.done}
                  aria-label={item.text}
                  onClick={() => update(open.id, (items) => items.map((i) => (i.id === item.id ? { ...i, done: !i.done } : i)))}
                  className={cn(
                    "flex size-6 shrink-0 items-center justify-center rounded-full border-[1.5px] transition-all duration-200 active:scale-90",
                    item.done ? cn(open.color, "border-transparent text-white") : "border-(color:--ios-label3)"
                  )}
                >
                  <Check className={cn("size-3.5 transition-opacity", item.done ? "opacity-100" : "opacity-0")} strokeWidth={4} />
                </button>
                <span className={cn("text-[17px] transition-colors duration-300", item.done && "text-(color:--ios-label3) line-through")}>
                  {item.text}
                </span>
              </li>
            ))}
            <li className="flex items-center gap-3 py-2.5">
              <span className="size-6 shrink-0 rounded-full border-[1.5px] border-(color:--ios-label3)" />
              <form
                className="flex-1"
                onSubmit={(event) => {
                  event.preventDefault()
                  const text = draft.trim()
                  if (!text) return
                  update(open.id, (items) => [...items, { id: items.length + 1, text, done: false }])
                  setDraft("")
                }}
              >
                <input
                  value={draft}
                  onChange={(event) => setDraft(event.target.value)}
                  placeholder="New Reminder"
                  aria-label="New reminder"
                  className="w-full bg-transparent text-[17px] outline-none placeholder:text-(color:--ios-label3)"
                />
              </form>
            </li>
          </ul>
        </IOSPage>
      </IOSDevice>
    )
  }

  const count = (list: List) => list.items.filter((item) => !item.done).length

  return (
    <IOSDevice>
      <IOSPage title="Reminders" large={false} right={<IOSNavButton>Edit</IOSNavButton>}>
        <div className="grid grid-cols-2 gap-3 px-4 pt-2 pb-6">
          {smart.map(({ label, icon: Icon, color, count: n }) => (
            <button
              key={label}
              type="button"
              className="flex flex-col gap-1 rounded-[16px] bg-(color:--ios-surface) p-3 text-left transition-transform active:scale-[0.97]"
            >
              <span className="flex items-start justify-between">
                <span className={cn("flex size-[34px] items-center justify-center rounded-full text-white", color)}>
                  <Icon className="size-[18px]" />
                </span>
                <span className="text-[30px] leading-[34px] font-bold tabular-nums">{n}</span>
              </span>
              <span className="text-[16px] font-semibold text-(color:--ios-label2)">{label}</span>
            </button>
          ))}
        </div>
        <IOSGroup header="My Lists">
          {lists.map((list) => {
            const Icon = listIcons[list.id as keyof typeof listIcons]
            return (
              <IOSRow
                key={list.id}
                icon={<Icon />}
                iconClassName={cn(list.color, "rounded-full")}
                title={list.name}
                detail={count(list)}
                chevron
                onClick={() => setOpenId(list.id)}
              />
            )
          })}
        </IOSGroup>
      </IOSPage>
    </IOSDevice>
  )
}
```
