This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Package manager is pnpm (CI uses pnpm 12, latest Node LTS). Do not use npm or yarn.
| Task | Command |
|---|---|
| Install deps (also wires the playground to the local source via the workspace) | pnpm install |
| Run the playground (interactive demo / dev harness) | pnpm start |
| Open Cypress component-test UI | pnpm test |
| Run Cypress component tests headlessly (CI uses this) | pnpm test:run |
Build the library (tsdown + copy style.css → dist/ReactToastify.css) |
pnpm build |
Format src/ with Prettier |
pnpm prettier |
Tests are Cypress component tests, not Jest. Files live next to source as *.cy.tsx (e.g. src/components/Toast.cy.tsx). To run one file headlessly:
pnpm exec cypress run --component -b chrome --spec src/components/Toast.cy.tsxOr use pnpm test to open the interactive runner and pick a spec.
There is no ESLint. Code style is Prettier only (config inline in package.json: printWidth: 120, single quotes, no trailing commas, avoid arrow parens). A lefthook pre-commit hook runs lint-staged, which runs Prettier on staged *.{js,jsx,ts,tsx,md,html,css} files. The CI workflow's lint step is currently commented out — Prettier is the only gate.
tsdown.config.mts produces three independent bundles:
react-toastify(default) —src/index.ts. ItsToastContainerexport is theStyledToastContainerwrapper atsrc/components/StyledToastContainer.tsx, which importssrc/style.cssas a string (via Vite's?rawsuffix; tsdown uses a small Rolldown plugin that loads*.css?rawas a string) and injects it viauseStyleSheet(css, props.nonce)on mount. Users can pass anonceprop for Content Security Policy compliance. All bundles are prefixed with"use client";for React Server Components.react-toastify/unstyled—src/unstyled.ts. Re-exports the rawToastContainerfrom./componentswithout the styled wrapper, so nothing is injected at runtime. Use this subpath when consumers want to ship their own CSS (fromreact-toastify/dist/ReactToastify.cssor elsewhere).react-toastify/addons/use-notification-center— built from the internal workspace package atpackages/use-notification-center/into the/addonsdirectory (not/dist); declared inpackage.json#exports.
The raw stylesheet is also exposed as react-toastify/dist/ReactToastify.css.
The library is not Context- or Redux-based. The flow is:
src/core/store.ts— a module-level singleton. Holds aMap<containerId, ContainerObserver>and arenderQueuethat bufferstoast()calls issued before any<ToastContainer>mounts.src/core/toast.ts— the public imperative API (toast(),toast.success,toast.update,toast.promise,toast.onChange, …). Dispatches through the store.src/core/containerObserver.ts— one instance per<ToastContainer>. Owns per-container state: toastMap, waitingqueue(whenlimit > 0), snapshot foruseSyncExternalStore, prop validation, and lifecycle callbacks (onOpen/onClose).src/hooks/useToastContainer.ts— the only bridge to React.<ToastContainer>subscribes to its observer viauseSyncExternalStore, which is why nothing in the tree needs to re-render just because a toast was pushed.
Consequence: multiple <ToastContainer> instances are supported via containerId; the store routes toasts to the right container (default id is 1).
ToastContainer.tsx— positions the portal, manages stacking (CSS-variable transforms computed in a layout effect), keyboard focus /Alt+Thotkey, collapse state.Toast.tsx— per-toast wrapper; consumesuseToast(drag-to-dismiss, pause-on-hover, pause-on-blur, timer).ProgressBar.tsx,CloseButton.tsx,Icons.tsx— the pieces a toast renders.Transitions.tsx—Bounce/Flip/Slide/Zoombuilt withcssTransitionfromsrc/utils/cssTransition.tsx.
useToastContainer.ts— store → React bridge (see above). The most important file in the repo.useToast.ts— per-toast UX behavior.useIsomorphicLayoutEffect.ts— SSR-safe layout effect.useStyleSheet.ts— runtime CSS injection (replaces the old tsup shim). Per-documentMapso multiple<ToastContainer>instances only inject once per document (shadow DOM / iframe safe). If a later mount supplies a nonce and the previous injection had none, the attribute is updated on the existing<style>tag — seesrc/hooks/useStyleSheet.ts.
ToastContainerProps.limit— excess toasts are buffered in a per-container queue insidecontainerObserver.ts; as toasts close, the next queued toast is emitted.toast.clearWaitingQueue()drains it.ToastContainerProps.stacked—ToastContainer.tsxmeasures toast heights in a layout effect and sets CSS custom properties (--y,--g,--s) that drivetranslate3d-based stacking and a collapsed state.
Public types are centralized in src/types.ts and re-exported from src/index.ts. Notable: ToastOptions<Data>, UpdateOptions<Data>, ToastContainerProps, ToastContent<T>, ToastItem<Data> (payload for toast.onChange), TypeOptions, Theme.
Lives at packages/use-notification-center/ as a private pnpm workspace package (see its package.json — "private": true). It exports a useNotificationCenter() hook — a persistent notification store independent from the transient toast queue. It subscribes to toast.onChange() to capture lifecycle events and provides filtering, read/unread, and sort.
The addon declares "react-toastify": "workspace:*" as a dependency, so its source can import 'react-toastify' and pnpm resolves it via a symlink at packages/use-notification-center/node_modules/react-toastify → <repo root>. No Vite alias or tsconfig paths entry is needed.
It is not published separately. The root tsdown.config.mts builds it into /addons/use-notification-center/ at the repo root, which the main react-toastify package includes via package.json#files and exposes via the react-toastify/addons/use-notification-center and react-toastify/notification-center subpath exports. Consumers still install only react-toastify.
/playground is a standalone Vite app used as the demo and manual-QA harness. It is declared as a workspace in pnpm-workspace.yaml and depends on the root package via "react-toastify": "workspace:*", so pnpm install symlinks the root into playground/node_modules/react-toastify. pnpm start runs it (via pnpm --filter playground dev). Most changes should be verified in the playground before running Cypress.
pnpm-workspace.yaml lists:
- root (
react-toastify) — the published library. packages/*— currently justpackages/use-notification-center/(private internal addon package, see Addon section above).playground— dev harness.
Both playground and packages/use-notification-center depend on the root via workspace:*. pnpm install is the only setup step — no pnpm link required.
.github/workflows/build.yaml runs on every push and PR: install → pnpm build → pnpm test:run → upload Cypress screenshots/videos on failure → report coverage to Coveralls. Coverage is instrumented at runtime by vite-plugin-istanbul (see vite.config.mts) and collected by @cypress/code-coverage.