Skip to content

Conversation

@ChaserZ98
Copy link
Contributor

@ChaserZ98 ChaserZ98 commented Aug 17, 2025

📝 Description

This PR changes the endContent attribute of the ToastProps. It can now not only receive a variable of type ReactNode, but also a function with a onClose param and a return type of ReactNode. The functionality of the onClose function is the same as the toast's close button's onPress event. When users call the onClose function, it closes the toast just like clicking on the close button.

With this PR, Users can now create customized close button in the endContent.

⛳️ Current behavior (updates)

There is no way to create a close button in endContent. Although closeToast api provides a way to programmtically close a toast, it requires the toast's id which users can only access it after the toast is created. Since the toast's id won't be available when the user defines the endContent, there is no way to utilize the closeToast api to close itself.

🚀 New behavior

As mentioned in the description, users can now utilize the onClose function to customize a close button or whatever ui effect they may want within the endContent.

💣 Is this a breaking change (Yes/No):

No

📝 Additional Information

Please see the storybook example With End Content Close Function for demo. I am not sure if a demo in the doc is required. If yes, let me know and I will add it.

Summary by CodeRabbit

  • New Features

    • Toast endContent now accepts a function that receives an onClose callback, allowing custom actions (e.g., buttons) to dismiss the toast; existing ReactNode usage remains supported.
  • Documentation

    • Added “Custom Close Button in endContent” demo and updated Toast Props docs to show the new endContent function form with an onClose callback.

@ChaserZ98 ChaserZ98 requested a review from jrgarciadev as a code owner August 17, 2025 12:09
@changeset-bot
Copy link

changeset-bot bot commented Aug 17, 2025

🦋 Changeset detected

Latest commit: 41cda14

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@heroui/toast Patch
@heroui/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Aug 17, 2025

@ChaserZ98 is attempting to deploy a commit to the HeroUI Inc Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 17, 2025

Walkthrough

Adds function-based support for toast endContent that receives an onClose callback, updates toast rendering and typing, adds stories and docs demonstrating a custom close button in endContent, and includes a patch changeset for @heroui/toast.

Changes

Cohort / File(s) Summary
Release changeset
.changeset/grumpy-boxes-doubt.md
Adds a patch changeset for @heroui/toast describing that endContent can close the toast via onClose.
Toast component logic
packages/components/toast/src/toast.tsx
Adds customEndContent: if endContent is a function, invokes it with the close-button onPress handler; otherwise uses the node. Renders customEndContent alongside the Close button.
Toast types
packages/components/toast/src/use-toast.ts
Extends ToastProps.endContent type to `ReactNode
Stories
packages/components/toast/stories/toast.stories.tsx
Makes endContent configurable via story args; updates WithEndContent to use args.endContent; adds WithEndContentCloseFunction demonstrating a function endContent that invokes the provided onClose.
Docs examples & index
apps/docs/content/components/toast/custom-close-button-in-endContent.raw.jsx, apps/docs/content/components/toast/custom-close-button-in-endContent.ts, apps/docs/content/components/toast/index.ts
Adds a demo App showing a custom close button rendered via function endContent, exposes it in the docs content index.
Docs page
apps/docs/content/docs/components/toast.mdx
Inserts "Custom Close Button in endContent" demo and updates endContent prop type in the API docs to include the (onClose) => ReactNode signature.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EndContent as endContent(fn)
  participant Toast
  participant CloseBtn as CloseButton(onPress)

  Note over Toast,EndContent: Toast renders CloseButton and endContent(fn)
  User->>EndContent: Click custom action (calls onClose)
  EndContent->>Toast: onClose()
  Toast->>CloseBtn: invoke onPress (close flow)
  CloseBtn-->>Toast: dismissal sequence
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

👀 Status: To Review

Suggested reviewers

  • jrgarciadev
  • wingkwong
  • macci001

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 85e7897 and 41cda14.

📒 Files selected for processing (2)
  • apps/docs/content/components/toast/custom-close-button-in-endContent.raw.jsx (1 hunks)
  • apps/docs/content/docs/components/toast.mdx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/docs/content/components/toast/custom-close-button-in-endContent.raw.jsx
  • apps/docs/content/docs/components/toast.mdx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Prettier
  • GitHub Check: Continuous Release
  • GitHub Check: TypeScript
  • GitHub Check: Tests
  • GitHub Check: ESLint
  • GitHub Check: Build
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
packages/components/toast/src/use-toast.ts (1)

68-71: Type expansion looks good; update the JSDoc to describe the function variant

The union is correct and non-breaking. Please update the JSDoc so consumers discover the new function form via IntelliSense.

Apply this diff to improve the comment:

   /**
-   * Content to be displayed in the end side of the toast
+   * Content displayed at the end side of the toast.
+   * You can also provide a function that receives an `onClose` callback
+   * which, when called, will close this toast.
    */
   endContent?: ReactNode | ((onClose: () => void) => ReactNode);

Additionally, consider exposing a dedicated close() callback from this hook (mirroring the close button behavior) to avoid downstream casting of event handlers. See my suggestion in toast.tsx.

packages/components/toast/src/toast.tsx (2)

85-89: Avoid casting onPress to () => void; pass a dedicated close callback instead

Grabbing onPress from getCloseButtonProps() and casting to a no-arg function works at runtime but is type-unsafe and couples endContent to Button’s press event signature. Prefer exposing a zero-arg close() from useToast and passing that to the endContent function. This removes the cast, reduces re-computation, and decouples concerns.

Apply this diff here (assuming close is returned from useToast):

-  const customEndContent =
-    typeof endContent === "function"
-      ? endContent((getCloseButtonProps() as ButtonProps).onPress as () => void)
-      : endContent;
+  const customEndContent =
+    typeof endContent === "function" ? endContent(close) : endContent;

Outside the selected range (for context), add close in useToast (packages/components/toast/src/use-toast.ts):

// inside useToast(...)
const close = useCallback(() => {
  setIsToastExiting(true);
  setTimeout(() => document.body.focus(), 0);
  onClose?.();
}, [onClose]);

// include `close` in the returned object
return {
  // ...existing
  close,
};

Optionally, simplify getCloseButtonProps to reuse the same logic:

const getCloseButtonProps: PropGetter = useCallback(
  (props = {}) => ({
    className: slots.closeButton({class: classNames?.closeButton}),
    "aria-label": "closeButton",
    "data-hidden": dataAttr(hideCloseButton),
    ...mergeProps(props, {onPress: close}),
  }),
  [close],
);

112-112: Render order check: confirm intended tab order and semantics

Placing customEndContent after the close button changes keyboard tab order. If endContent contains primary actions, consider placing it before the close button for better flow, or ensure focus order is intentional.

packages/components/toast/stories/toast.stories.tsx (2)

146-146: Passing args.endContent through is correct; type the template for safety

This is fine. To keep stories type-safe with the new union, consider typing the template as const WithEndContentTemplate = (args: ToastProps) => { ... } so Storybook args conform to the updated ToastProps.

Outside the selected range:

const WithEndContentTemplate = (args: ToastProps) => {
  // ...
};

451-466: Great example of the function form; add a quick type hint for clarity

This nicely demonstrates receiving onClose and wiring it to an action. For readability in examples, you could annotate the param type.

Outside the selected range:

endContent: (onClose: () => void) => (
  <>
    {/* ... */}
  </>
)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8e51a85 and e53d134.

📒 Files selected for processing (4)
  • .changeset/grumpy-boxes-doubt.md (1 hunks)
  • packages/components/toast/src/toast.tsx (2 hunks)
  • packages/components/toast/src/use-toast.ts (1 hunks)
  • packages/components/toast/stories/toast.stories.tsx (2 hunks)
🔇 Additional comments (2)
.changeset/grumpy-boxes-doubt.md (1)

1-6: Changelog entry reads well and matches the change

Patch bump for @heroui/toast with a concise description. All good.

packages/components/toast/stories/toast.stories.tsx (1)

443-449: LGTM: static endContent example

Good example demonstrating the ReactNode variant for endContent.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 17, 2025

Open in StackBlitz

@heroui/accordion

npm i https://pkg.pr.new/@heroui/accordion@5605

@heroui/alert

npm i https://pkg.pr.new/@heroui/alert@5605

@heroui/autocomplete

npm i https://pkg.pr.new/@heroui/autocomplete@5605

@heroui/avatar

npm i https://pkg.pr.new/@heroui/avatar@5605

@heroui/badge

npm i https://pkg.pr.new/@heroui/badge@5605

@heroui/breadcrumbs

npm i https://pkg.pr.new/@heroui/breadcrumbs@5605

@heroui/button

npm i https://pkg.pr.new/@heroui/button@5605

@heroui/calendar

npm i https://pkg.pr.new/@heroui/calendar@5605

@heroui/card

npm i https://pkg.pr.new/@heroui/card@5605

@heroui/checkbox

npm i https://pkg.pr.new/@heroui/checkbox@5605

@heroui/chip

npm i https://pkg.pr.new/@heroui/chip@5605

@heroui/code

npm i https://pkg.pr.new/@heroui/code@5605

@heroui/date-input

npm i https://pkg.pr.new/@heroui/date-input@5605

@heroui/date-picker

npm i https://pkg.pr.new/@heroui/date-picker@5605

@heroui/divider

npm i https://pkg.pr.new/@heroui/divider@5605

@heroui/drawer

npm i https://pkg.pr.new/@heroui/drawer@5605

@heroui/dropdown

npm i https://pkg.pr.new/@heroui/dropdown@5605

@heroui/form

npm i https://pkg.pr.new/@heroui/form@5605

@heroui/image

npm i https://pkg.pr.new/@heroui/image@5605

@heroui/input

npm i https://pkg.pr.new/@heroui/input@5605

@heroui/input-otp

npm i https://pkg.pr.new/@heroui/input-otp@5605

@heroui/kbd

npm i https://pkg.pr.new/@heroui/kbd@5605

@heroui/link

npm i https://pkg.pr.new/@heroui/link@5605

@heroui/listbox

npm i https://pkg.pr.new/@heroui/listbox@5605

@heroui/menu

npm i https://pkg.pr.new/@heroui/menu@5605

@heroui/modal

npm i https://pkg.pr.new/@heroui/modal@5605

@heroui/navbar

npm i https://pkg.pr.new/@heroui/navbar@5605

@heroui/number-input

npm i https://pkg.pr.new/@heroui/number-input@5605

@heroui/pagination

npm i https://pkg.pr.new/@heroui/pagination@5605

@heroui/popover

npm i https://pkg.pr.new/@heroui/popover@5605

@heroui/progress

npm i https://pkg.pr.new/@heroui/progress@5605

@heroui/radio

npm i https://pkg.pr.new/@heroui/radio@5605

@heroui/ripple

npm i https://pkg.pr.new/@heroui/ripple@5605

@heroui/scroll-shadow

npm i https://pkg.pr.new/@heroui/scroll-shadow@5605

@heroui/select

npm i https://pkg.pr.new/@heroui/select@5605

@heroui/skeleton

npm i https://pkg.pr.new/@heroui/skeleton@5605

@heroui/slider

npm i https://pkg.pr.new/@heroui/slider@5605

@heroui/snippet

npm i https://pkg.pr.new/@heroui/snippet@5605

@heroui/spacer

npm i https://pkg.pr.new/@heroui/spacer@5605

@heroui/spinner

npm i https://pkg.pr.new/@heroui/spinner@5605

@heroui/switch

npm i https://pkg.pr.new/@heroui/switch@5605

@heroui/table

npm i https://pkg.pr.new/@heroui/table@5605

@heroui/tabs

npm i https://pkg.pr.new/@heroui/tabs@5605

@heroui/toast

npm i https://pkg.pr.new/@heroui/toast@5605

@heroui/tooltip

npm i https://pkg.pr.new/@heroui/tooltip@5605

@heroui/user

npm i https://pkg.pr.new/@heroui/user@5605

@heroui/react

npm i https://pkg.pr.new/@heroui/react@5605

@heroui/system

npm i https://pkg.pr.new/@heroui/system@5605

@heroui/system-rsc

npm i https://pkg.pr.new/@heroui/system-rsc@5605

@heroui/theme

npm i https://pkg.pr.new/@heroui/theme@5605

@heroui/use-aria-accordion

npm i https://pkg.pr.new/@heroui/use-aria-accordion@5605

@heroui/use-aria-accordion-item

npm i https://pkg.pr.new/@heroui/use-aria-accordion-item@5605

@heroui/use-aria-button

npm i https://pkg.pr.new/@heroui/use-aria-button@5605

@heroui/use-aria-link

npm i https://pkg.pr.new/@heroui/use-aria-link@5605

@heroui/use-aria-modal-overlay

npm i https://pkg.pr.new/@heroui/use-aria-modal-overlay@5605

@heroui/use-aria-multiselect

npm i https://pkg.pr.new/@heroui/use-aria-multiselect@5605

@heroui/use-aria-overlay

npm i https://pkg.pr.new/@heroui/use-aria-overlay@5605

@heroui/use-callback-ref

npm i https://pkg.pr.new/@heroui/use-callback-ref@5605

@heroui/use-clipboard

npm i https://pkg.pr.new/@heroui/use-clipboard@5605

@heroui/use-data-scroll-overflow

npm i https://pkg.pr.new/@heroui/use-data-scroll-overflow@5605

@heroui/use-disclosure

npm i https://pkg.pr.new/@heroui/use-disclosure@5605

@heroui/use-draggable

npm i https://pkg.pr.new/@heroui/use-draggable@5605

@heroui/use-form-reset

npm i https://pkg.pr.new/@heroui/use-form-reset@5605

@heroui/use-image

npm i https://pkg.pr.new/@heroui/use-image@5605

@heroui/use-infinite-scroll

npm i https://pkg.pr.new/@heroui/use-infinite-scroll@5605

@heroui/use-intersection-observer

npm i https://pkg.pr.new/@heroui/use-intersection-observer@5605

@heroui/use-is-mobile

npm i https://pkg.pr.new/@heroui/use-is-mobile@5605

@heroui/use-is-mounted

npm i https://pkg.pr.new/@heroui/use-is-mounted@5605

@heroui/use-measure

npm i https://pkg.pr.new/@heroui/use-measure@5605

@heroui/use-pagination

npm i https://pkg.pr.new/@heroui/use-pagination@5605

@heroui/use-real-shape

npm i https://pkg.pr.new/@heroui/use-real-shape@5605

@heroui/use-ref-state

npm i https://pkg.pr.new/@heroui/use-ref-state@5605

@heroui/use-resize

npm i https://pkg.pr.new/@heroui/use-resize@5605

@heroui/use-safe-layout-effect

npm i https://pkg.pr.new/@heroui/use-safe-layout-effect@5605

@heroui/use-scroll-position

npm i https://pkg.pr.new/@heroui/use-scroll-position@5605

@heroui/use-ssr

npm i https://pkg.pr.new/@heroui/use-ssr@5605

@heroui/use-theme

npm i https://pkg.pr.new/@heroui/use-theme@5605

@heroui/use-update-effect

npm i https://pkg.pr.new/@heroui/use-update-effect@5605

@heroui/use-viewport-size

npm i https://pkg.pr.new/@heroui/use-viewport-size@5605

@heroui/aria-utils

npm i https://pkg.pr.new/@heroui/aria-utils@5605

@heroui/dom-animation

npm i https://pkg.pr.new/@heroui/dom-animation@5605

@heroui/framer-utils

npm i https://pkg.pr.new/@heroui/framer-utils@5605

@heroui/react-rsc-utils

npm i https://pkg.pr.new/@heroui/react-rsc-utils@5605

@heroui/react-utils

npm i https://pkg.pr.new/@heroui/react-utils@5605

@heroui/shared-icons

npm i https://pkg.pr.new/@heroui/shared-icons@5605

@heroui/shared-utils

npm i https://pkg.pr.new/@heroui/shared-utils@5605

@heroui/stories-utils

npm i https://pkg.pr.new/@heroui/stories-utils@5605

@heroui/test-utils

npm i https://pkg.pr.new/@heroui/test-utils@5605

commit: 41cda14

Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just use closeToast?

@ChaserZ98
Copy link
Contributor Author

ChaserZ98 commented Aug 17, 2025

Because, like I mentioned, you need the toast id to close it and it won't be available until you call addToast.

You might be able to implement it using a reference. It might be easy to manage the reference for one toast but when it comes to many toasts, that would be complicated. So exposing the onClose to components in endContent would be a much easier way, just like how users are using onClose within the ModalContent to close the modal.

@vercel
Copy link

vercel bot commented Aug 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
heroui Ready Ready Preview Comment Aug 30, 2025 0:34am
heroui-sb Ready Ready Preview Comment Aug 30, 2025 0:34am

Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please update doc part as well

@ChaserZ98 ChaserZ98 force-pushed the feat/toast-close-in-endContent branch from e53d134 to 85e7897 Compare September 3, 2025 20:17
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e53d134 and 85e7897.

📒 Files selected for processing (8)
  • .changeset/grumpy-boxes-doubt.md (1 hunks)
  • apps/docs/content/components/toast/custom-close-button-in-endContent.raw.jsx (1 hunks)
  • apps/docs/content/components/toast/custom-close-button-in-endContent.ts (1 hunks)
  • apps/docs/content/components/toast/index.ts (2 hunks)
  • apps/docs/content/docs/components/toast.mdx (1 hunks)
  • packages/components/toast/src/toast.tsx (2 hunks)
  • packages/components/toast/src/use-toast.ts (1 hunks)
  • packages/components/toast/stories/toast.stories.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • .changeset/grumpy-boxes-doubt.md
  • packages/components/toast/src/toast.tsx
  • packages/components/toast/src/use-toast.ts
  • packages/components/toast/stories/toast.stories.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/docs/content/components/toast/custom-close-button-in-endContent.ts (2)
packages/utilities/shared-utils/scripts/postinstall.js (1)
  • react (28-28)
apps/docs/content/components/toast/custom-close-button-in-endContent.raw.jsx (1)
  • App (3-21)
🔇 Additional comments (2)
apps/docs/content/components/toast/index.ts (1)

9-21: Docs wiring LGTM

Importing and exposing customCloseButtonInEndContent in toastContent looks correct.

apps/docs/content/components/toast/custom-close-button-in-endContent.ts (1)

1-9: Mapping export looks good

The raw example is correctly exposed for CodeDemo consumption.

@ChaserZ98 ChaserZ98 requested a review from wingkwong September 3, 2025 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants