Problems with the duration of Shadcn Toasts (Inspired by the react-hot-toast library)

Within a 13.4 Nextjs project (app router), utilizing Typescript and TailwindCSS.

I am currently exploring the usage of toasts provided by the remarkable shadcnUI Library, which draws inspiration from react-hot-toast while adding its own unique flair.

Implementing the toast's core functionalities posed no issues for me initially. However, things took a turn when I attempted to include a timer bar at the bottom of each toast to display the duration of the toast.

I was able to successfully display the timer bar (a customized progress bar element) and synchronize it with the duration of each toast.

During this process, I noticed that the actual duration of the toast did not align with the props I assigned to it or the value designated in the TOAST_REMOVE_DELAY constant within the use-toast.ts file of the shadcn component.

I experimented with various configurations, only to discover that the Toaster had a default duration of 5000ms (5s), set by the root toaster inherited from react-hot-toast. Surprisingly, the "duration" value provided as props within the individual Toast component did not override this default setting.

After tinkering with the code extensively, I struggled to pass the duration prop effectively and make it the sole determinant of the toast's duration (excluding null values, where the default value is applied).

To anyone willing to offer assistance...

Here is the use toast:

import * as React from "react"

import type {
  ToastActionElement,
  ToastProps,
} from "@/components/ui/toast"

const TOAST_LIMIT = 3
export const TOAST_REMOVE_DELAY = 2000

type ToasterToast = ToastProps & {
  id: string
  title?: React.ReactNode
  description?: React.ReactNode
  action?: ToastActionElement
  duration?: number
}
...

Here is the Toaster:

/** @format */

'use client';

import {
  Toast,
  ToastClose,
  ToastDescription,
  ToastProvider,
  ToastTitle,
  ToastViewport,
} from '@/components/ui/toast';
import { useToast } from '@/components/ui/use-toast';
import { TimerBar } from '@/components/ui/timerBar';
import { TOAST_REMOVE_DELAY } from '@/components/ui/use-toast';
...

Here is the Toast (not crucial but valuable for replication):

/** @format */

import * as React from 'react';
import * as ToastPrimitives from '@radix-ui/react-toast';
import { cva, type VariantProps } from 'class-variance-authority';
import { Icon } from '@/components/icons/icons';
import { cn } from '@/lib/helpers/utils';
...

<p>I've explored different options:</p>
<ul>
<li>Directly modifying the TOAST_REMOVE_DELAY constant (to no avail)</li>
<li>Incorporating an additional timer within the setTimeout function of addToRemoveQueue</li>
</ul>
<pre><code>const addToRemoveQueue = (toastId: string, delay: number) => {
  if (toastTimeouts.has(toastId)) {
    return
  }
...

It seems even the default TOAST_REMOVE_DELAY does not impact the toast duration. Although set to 1000000, the toasts persist for only 5000 milliseconds each.

  • I attempted to introduce another timer triggering the DISMISS_TOAST action, yet it only functions for durations shorter than 5 seconds. Beyond that, the Toaster's built-in timer overrides the custom setup.

  • Considering whether this issue may be specific to Development Mode, I deployed the solution into production with similar outcomes.

Answer №1

It seems that I overlooked passing the prop duration in the Toaster component:

export function Toaster() {
  const { toasts } = useToast();

  return (
    <div className="absolute max-w-full">
<ToastProvider
      duration={TOAST_REMOVE_DELAY}>
        {toasts.map(function ({
          id,
          title,
          description,
          action,
          duration,
          ...props
        }) {
          return (
            <Toast key={id} **duration={duration}** {...props}>
              <div className="flex flex-col w-full">
                <div className="flex justify-start items-center w-full p-2 gap-2 mr-4">
                  <div className="grid gap-1">
                    {title && <ToastTitle>{title}</ToastTitle>}
                    {description && (
                      <ToastDescription>{description}</ToastDescription>
                    )}
                  </div>
                  {action}
                </div>
                <ToastClose />
                <TimerBar max={duration || TOAST_REMOVE_DELAY} />
              </div>
            </Toast>
          );
        })}
        <ToastViewport />
      </ToastProvider>
    </div>
  );
}

If you're interested, here are the corrected files: use-toast:

// Code inspired by react-hot-toast library
import * as React from "react"
import type {
  ToastActionElement,
  ToastProps,
} from "@/components/ui/toast"

const TOAST_LIMIT = 3
export const TOAST_REMOVE_DELAY = 3000

type ToasterToast = ToastProps & {
  id: string
  title?: React.ReactNode
  description?: React.ReactNode
  action?: ToastActionElement
  duration?: number
}

// Rest of the script contains details about toast actions and reducers but has been omitted for brevity.

To test it out, add the Toaster within ToastProvider in your RootLayout:

return (
    <NextAuthSessionProvider session={session}>
      <html lang="en">
        <body
          className={`${circularBold.variable} ${gambettaReg.variable} ${gambettaSemi.variable} ${nunito.variable} ${inter.variable} font-inter`}
        >
          <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
            <main className="flex flex-col justify-between items-center min-h-screen min-w-screen">
              <Header user={session?.user} />
              {children}
              <Footer />
              **<Toaster />**
            </main>
          </ThemeProvider>
        </body>
      </html>
    </NextAuthSessionProvider>
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Struggling with passing a function along with parameters to a component in React has been a challenge for me

Currently utilizing React in conjunction with NextJS My goal is to send a function, along with its parameters, to my 'Alerts' component so that it can wait for user input before executing the function. For instance, prior to clearing a list, I ...

An easy guide on incorporating npm packages into a typescript project

In my TypeScript project, I am encountering an issue with the following imports: import * as schedule from "node-schedule"; import * as ACTIONS from "../../../actions"; The second import resolves successfully, but the first one does no ...

Every time I clear the information, it seems to be instantly replaced with new data. How can I prevent it from constantly refilling?

When I press the remove button on my application, it successfully deletes the data in a field. However, it also automatically adds new data to the field which was not intended. I am seeking assistance on how to keep those fields empty after removing the ...

React function causing website to freeze upon dispatch

I created a function in the child component to handle checkbox selection and trigger setDispatch(true). Unfortunately, whenever I check the checkbox, the website freezes and stops responding until I close and reopen it. Here is the function: const [ ...

Implement FieldResolver in TypeGraphQL for an array of objects

My current dilemma revolves around a specific issue related to the definition of my Cart type, which is structured as follows: @ObjectType() export class Cart { @Field(() => ID) id: string; @Field((_type) => String) ownerId: String ...

Tips for incorporating "are you sure you want to delete" into Reactjs

I am currently working with Reactjs and Nextjs. I have a list of blogs and the functionality to delete any blog. However, I would like to display a confirmation message before deleting each item that says "Are you sure you want to delete this item?" How ...

insert a gap between two elements in the identical line

Is there a way to create spacing between two text fields in the same row? I have tried using margins, paddings, and display flex in the css file but haven't been successful. import "./styles.css"; import TextField from "@material-ui/cor ...

Typescript error in RxJS: Incorrect argument type used

I came across this code snippet from an example in rxjs: Observable.fromEvent(this.getNativeElement(this.right), 'click') .map(event => 10) .startWith({x: 400, y: 400}) .scan((acc, curr) => Object.assign({}, acc, {x: acc ...

The ConsoleCapture does not capture every console error for Sentry

Running into an issue capturing console errors with Sentry in a Next.js app. The problem arises from an error within a library that is inaccessible to us, specifically related to WebSocket "WebSocket is already in CLOSING or CLOSED state" This error is c ...

Setting metadata dynamically in a NextJs 14 app router from the root layout file: A step-by-step guide

Utilizing Redux Toolkit to manage data in my application, each page has its own distinct state. At the time of page load, the state is fetched or populated along with metadata. Currently, I am looking to set Metadata based on the information available in ...

"Concealing a column in a PrimeNG data table with dynamic columns: A step-by-step

Hi, I'm looking for a way to hide a column in my PrimeNG data table. Is there an attribute that can be used to turn off columns in PrimeNG data tables? .Html <p-dataTable emptyMessage="{{tbldatamsg}}" [value]="dataset" scrollable="true" [style]=" ...

"Although the NextJS client-side data is present, it seems to be struggling to

I am facing an issue while trying to display fetched data on a blog page. Although the data is successfully retrieved client-side and I can view it using console.log(), it does not appear on the page and the elements remain empty. I am completely puzzled. ...

Exploring the TypeScript compiler API to read and make updates to objects is an interesting

I'm delving into the world of the typescript compiler API and it seems like there's something I am overlooking. I am trying to find a way to update a specific object in a .ts file using the compiler API. Current file - some-constant.ts export co ...

Navigate to the dynamic route using regular expressions in the NextJS configuration file

I encountered an issue while building a new website. In my old site, there were some pages with a child route /feed. For instance: www.mypage.com/blogs/blog-1/feed My goal is to redirect from /blogs/blog-1/feed to /blogs/blog-1, but I keep getting a 404 ...

Discover the data type without using the extends keyword within an interface in Typescript

I'm struggling with making my code declarative enough. I want to infer a type inside an interface scope and then use that same type as an argument for a method within the interface. Here is a simple example: interface Prop { x: infer U, // ^^ ...

Error encountered: NextJS version 14.0.3 - Uncaught error: NEXT_REDIRECT is not defined

I have a login page that needs to redirect to the app section after successful login, and the app section should redirect back to the login page if no user is logged in. The redirection in the AuthGuard component is functioning well: import { PropsWithChi ...

Utilizing getter and setter functions within a setter with a type guard

I need to implement a getter and setter in my class. The setter should accept a querySelector, while the getter is expected to return a new type called pageSections. The challenge I'm facing is that both the getter and setter must have the same argum ...

Determining in Angular 8 whether a value has been altered by a user or by a method call

Within my select element, the value is currently being assigned through an ngOnInit call. Here is an example of the HTML code: <select name="duration" [(ngModel)]="exercisePlan.duration" (ngModelChange)="onChange($event)"> <option *ngFor="l ...

Issue with Typescript typing for the onChange event

I defined my state as shown below: const [updatedStep, updateStepObj] = useState( panel === 'add' ? new Step() : { ...selectedStep } ); Additionally, I have elements like: <TextField ...

I am seeking advice on how to create an extension for a generic class in TypeScript specifically as a getter

Recently, I discovered how to create extensions in TypeScript: interface Array<T> { lastIndex(): number } Array.prototype.lastIndex = function (): number { return this.length - 1 } Now, I want to figure out how to make a getter from it. For exam ...