Skip to content

feat(alert): add dismiss transition, timing and duration props - #1683

Open
NokNok-0 wants to merge 1 commit into
themesberg:mainfrom
NokNok-0:feat/alert-transition-timing
Open

feat(alert): add dismiss transition, timing and duration props#1683
NokNok-0 wants to merge 1 commit into
themesberg:mainfrom
NokNok-0:feat/alert-transition-timing

Conversation

@NokNok-0

@NokNok-0 NokNok-0 commented Jul 27, 2026

Copy link
Copy Markdown

Summary

Adds transition, timing, and duration props to the <Alert> component so the dismiss animation can be customized, mirroring the transition, duration, and timing options of vanilla Flowbite's Dismiss class (see discussion #1091).

This closes the gap called out in #1092 between vanilla Flowbite's Alert and the React component, where the React <Alert> had no way to configure its dismiss animation.

New props

Prop Type Default Description
transition string (Tailwind class) transition-opacity Tailwind CSS transition utility class applied while dismissing
duration number (ms) 300 Length of the dismiss transition
timing string (Tailwind class) ease-out Tailwind CSS transition-timing-function utility class

onDismiss now fires after the transition has finished, or immediately when duration is 0 (preserving the previous synchronous behaviour). The transition classes are also exposed via the optional alert.transition theme key for global overrides.

Motivation

Users currently have no way to animate the dismissal of an <Alert>; it disappears instantly. Vanilla Flowbite exposes these options and they were requested back in #1091/#1092.

Related issues

Closes #1092.

Type of change

  • New feature (non-breaking change which adds functionality)

Breaking changes

None. All three new props are optional with defaults that match the existing instant-dismiss behaviour when duration is 0, and the existing onDismiss signature is unchanged.

Checklist

  • I have followed the Your First Code Contribution section of the Contributing guide
  • bun run format / bun run lint / bun run typecheck / bun run test:coverage / bun run build all pass locally
  • Added unit tests covering the new behaviour (default transition, custom props, custom theme, deferred onDismiss, immediate dismiss when duration === 0)
  • Updated the documentation with a new "Dismiss transition" section and a live example (alert.transition.tsx)

Implementation notes for reviewers

A few decisions worth flagging — happy to adjust if any of them don't fit the project's conventions:

  1. alert.transition is an optional theme key (no base defaults in theme.ts). The defaults live as prop defaults on the component, and the theme key only wins when a user explicitly sets it. This keeps the prop API authoritative while still allowing global overrides via <ThemeProvider>.
  2. onDismiss is invoked from a setTimeout and therefore receives a synthetic MouseEvent (new MouseEvent("click")) rather than the original click event. This keeps the existing onDismiss: (event) => void signature unchanged (no breaking change), but if you'd prefer I could split this into an explicit lifecycle (e.g. onDismissStart / onDismiss) instead. Let me know.
  3. The dismiss transition classes are applied via twMerge so users can override any of transition / duration-[Nms] / timing independently.

Notes on local test run

bun run test:coverage (the CI command) passes: 344 passed, 1 skipped, 1 todo. The standalone bun test src/helpers job has one pre-existing failure (withoutThemingProps > should remove theme, clearTheme and applyTheme props) caused by a mock leak from resolve-props.test.ts — this failure is present on main too and is unrelated to this change.

Summary by CodeRabbit

  • New Features

    • Added configurable dismiss transitions to the Alert component, with support for custom transition styles, timing, and duration.
    • onDismiss now triggers after the dismissal animation completes, or immediately when duration is set to 0.
    • Dismiss animations apply only during dismissal.
  • Documentation

    • Expanded Alert documentation with a new “Dismiss transition” section and usage details.
  • Examples

    • Added an Alert dismissal transition demo.
  • Tests

    • Added/updated tests covering transition classes, timing/duration behavior, themed overrides, and immediate dismissal.

@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 437ead4

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

@NokNok-0 is attempting to deploy a commit to the Bergside Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The Alert component now supports configurable dismissal transitions, delayed onDismiss callbacks, themed transition classes, tests for timing and styling, and a documented example.

Changes

Alert dismiss transition

Layer / File(s) Summary
Transition contract and dismissal behavior
packages/ui/src/components/Alert/Alert.tsx, packages/ui/src/components/Alert/Alert.test.tsx
Alert adds duration, timing, and transition props, applies dismissal classes, delays onDismiss, supports themed values, and covers these behaviors in tests.
Example and documentation
apps/web/examples/alert/..., apps/web/content/docs/components/alert.mdx
Adds a fade-dismissal example, exports it through the alert examples index, and documents the transition options and callback timing.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DismissButton
  participant Alert
  participant onDismissCallback
  DismissButton->>Alert: click dismiss
  Alert->>Alert: apply transition classes
  Alert->>Alert: wait for duration
  Alert->>onDismissCallback: invoke onDismiss
Loading

Possibly related PRs

Suggested labels: :books: documentation

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main Alert dismiss-transition enhancement.
Linked Issues check ✅ Passed The Alert component now supports timing, transition, and duration behavior requested by issue #1092.
Out of Scope Changes check ✅ Passed The PR stays focused on Alert behavior, docs, tests, and an example, with no unrelated changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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

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

Add 'transition', 'timing', and 'duration' props to the Alert component so the
dismiss animation can be customized, mirroring vanilla Flowbite's Dismiss class
options.

- transition (default: transition-opacity) — Tailwind transition utility class
- duration   (default: 300)               — length of the transition in ms
- timing     (default: ease-out)          — Tailwind timing-function utility class

onDismiss now fires after the transition has finished (or immediately when
duration is 0). The transition classes are also exposed via the optional
alert.transition theme key for global overrides.

Closes themesberg#1092
@NokNok-0
NokNok-0 force-pushed the feat/alert-transition-timing branch from 2b7fd9c to 437ead4 Compare July 27, 2026 18:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/components/Alert/Alert.tsx`:
- Around line 99-105: Update handleDismissClick in Alert to defer dismissal only
when duration > 0, so duration={0} invokes onDismiss synchronously even when the
default transition is present. Add or update the zero-duration test with a
synchronous assertion immediately after the click rather than relying on
awaiting user.click.
- Around line 121-123: Update the Alert component’s transition handling to
derive one effective numeric duration from the themed transition value,
including overrides such as duration-700, and reuse that value for both the
rendered transition class and the onDismiss timeout. Replace the independent
prop-default timer usage while preserving existing callback behavior.
- Around line 93-95: Update the dismiss flow in Alert’s handleDismissClick and
timeout callback to retain the original React mouse event and pass that same
event to onDismiss when the timer fires, removing the fabricated MouseEvent
while preserving the existing duration behavior.
- Around line 117-120: Update the transition class construction in the Alert
component’s twMerge call to use Tailwind’s arbitrary duration syntax, such as
duration-[${duration}ms] or the existing alert-duration CSS variable, so custom
durations are applied correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 615cdef3-0d7f-4ce0-a052-3c3e7cdb635c

📥 Commits

Reviewing files that changed from the base of the PR and between 85319bd and 2b7fd9c.

📒 Files selected for processing (5)
  • apps/web/content/docs/components/alert.mdx
  • apps/web/examples/alert/alert.transition.tsx
  • apps/web/examples/alert/index.ts
  • packages/ui/src/components/Alert/Alert.test.tsx
  • packages/ui/src/components/Alert/Alert.tsx

Comment on lines +93 to +95
const timeoutId = setTimeout(() => {
onDismiss?.(new MouseEvent("click") as unknown as React.MouseEvent<HTMLButtonElement>);
}, duration);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the Alert component around the reported lines.
if git ls-files 'packages/ui/src/components/Alert/Alert.tsx' | grep -q .; then
  echo "== file exists =="
  wc -l packages/ui/src/components/Alert/Alert.tsx
  echo "== relevant lines =="
  cat -n packages/ui/src/components/Alert/Alert.tsx | sed -n '1,180p'
else
  echo "Alert.tsx not found"
  fd -i 'Alert\.tsx$' .
fi

echo "== search onDismiss usages =="
rg -n "onDismiss|new MouseEvent|onDismiss\?\(" packages/ui/src || true

echo "== inspect package/tool config maybe relevant =="
fd -i "package.json|tailwind.config" . | sed -n '1,80p'

Repository: themesberg/flowbite-react

Length of output: 8677


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== inspect related test around onDismiss assertions =="
cat -n packages/ui/src/components/Alert/Alert.test.tsx | sed -n '120,225p'

echo "== TypeScript check around mock/expect behavior without compiling repo code? =="
echo "node version:"
node --version || true
node - <<'JS'
class SyntheticEvent {
  constructor(type, target, currentTarget) {
    this.type = type;
    this.target = target;
    this.currentTarget = currentTarget;
  }
}
const native = new MouseEvent("click");
const cast = native;
console.log(JSON.stringify({
  type: cast.type,
  target: cast.target,
  currentTarget: cast.currentTarget,
  stopPropagationType: typeof cast.stopPropagation,
}, null, 2));
console.log(JSON.stringify({
  syn: new SyntheticEvent("click", "button", "root").target,
  eventTarget: cast.target,
}, null, 2));
JS

Repository: themesberg/flowbite-react

Length of output: 4767


Pass the original dismiss click event to the deferred onDismiss callback.

The timed callback currently receives a fabricated native MouseEvent, so callers won’t get a React synthetic click event with the correct target/currentTarget. Keep the event from handleDismissClick and pass it through when the timeout runs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/Alert/Alert.tsx` around lines 93 - 95, Update the
dismiss flow in Alert’s handleDismissClick and timeout callback to retain the
original React mouse event and pass that same event to onDismiss when the timer
fires, removing the fabricated MouseEvent while preserving the existing duration
behavior.

Comment on lines +99 to +105
const handleDismissClick: ComponentProps<"button">["onClick"] = (event) => {
// When a transition is configured, play it before firing `onDismiss`.
if (transition || duration > 0) {
setIsDismissing(true);
return;
}
onDismiss?.(event);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make duration={0} synchronous.

transition defaults to "transition-opacity", so this condition is true even when duration is 0. The callback is therefore deferred through setTimeout(..., 0), contrary to the documented immediate behavior. The test's await user.click(...) masks this ordering bug.

Use duration > 0 as the deferral condition, and add a synchronous assertion for the zero-duration case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/Alert/Alert.tsx` around lines 99 - 105, Update
handleDismissClick in Alert to defer dismissal only when duration > 0, so
duration={0} invokes onDismiss synchronously even when the default transition is
present. Add or update the zero-duration test with a synchronous assertion
immediately after the click rather than relying on awaiting user.click.

Comment on lines +117 to +120
twMerge(
transition,
`duration-${duration}`,
timing,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)(Alert\.tsx|tailwind|postcss|package\.json|\.lint|eslint|stylelint|.*\.config\.(js|ts|mjs|cjs))$|Alert' || true

echo "== target snippet =="
if [ -f packages/ui/src/components/Alert/Alert.tsx ]; then
  nl -ba packages/ui/src/components/Alert/Alert.tsx | sed -n '1,180p'
fi

echo "== duration/static Tailwind config candidates =="
rg -n "duration-|transitionDuration| safelist|content|preset|plugins|tailwindcss" -S . -g '!node_modules' -g '!dist' -g '!build' -g '!coverage' | head -200

echo "== lint configs mentioning duration =="
rg -n "duration|transition|valid|invalid" -S . -g '*lnt*' -g '!node_modules' -g '!dist' -g '!build' | head -200 || true

Repository: themesberg/flowbite-react

Length of output: 1043


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Alert.tsx relevant lines =="
awk '{printf "%6d\t%s\n", NR, $0}' packages/ui/src/components/Alert/Alert.tsx | sed -n '105,130p'

echo "== Alert.tsx duration prop/types =="
awk '{printf "%6d\t%s\n", NR, $0}' packages/ui/src/components/Alert/Alert.tsx | sed -n '1,120p'

echo "== tailwind config duration/content =="
awk '{printf "%6d\t%s\n", NR, $0}' packages/ui/tailwind.config.cjs
echo "---"
awk '{printf "%6d\t%s\n", NR, $0}' apps/web/tailwind.config.cjs | sed -n '1,260p'

echo "== runtime duration values in Alert.tsx/usages =="
rg -n "duration|transitionDuration|twMerge\\(|duration-" packages/ui/src/components/Alert packages/ui/tailwind.config.cjs -S

echo "== lint rules mentioning invalid transition/duration =="
rg -n "transitionDuration|duration-|valid|Invalid|invalid|CSS variable|arbitrary" -S . -g '!node_modules' | head -200

Repository: themesberg/flowbite-react

Length of output: 40429


🌐 Web query:

Tailwind CSS arbitrary values duration- arbitrary value transition duration

💡 Result:

To apply an arbitrary transition duration in Tailwind CSS, use the square bracket syntax directly within the class name [1][2]. For example:

If you are using a CSS variable, you can use the same syntax [1]:
Alternatively, Tailwind provides a shorthand syntax for CSS variables using parentheses, which automatically wraps your value in the var function [1]:
This functionality allows you to generate one-off transition durations on the fly without needing to extend your theme configuration [2][3]. As with all arbitrary values, this syntax integrates seamlessly with responsive modifiers (e.g., md:duration-[500ms]) and interactive states (e.g., hover:duration-[1s]) [1][4][3].

Citations:


Use arbitrary transition duration values.

duration-${duration} generates classes like duration-300, which Tailwind does not treat as arbitrary values and won’t include for non-default/custom durations. Use duration-[${duration}ms] or duration-[var(--alert-duration)] instead.

🧰 Tools
🪛 GitHub Check: 🕵 Lint

[warning] 117-117:
Classname 'duration-' is not a Tailwind CSS class!

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/Alert/Alert.tsx` around lines 117 - 120, Update
the transition class construction in the Alert component’s twMerge call to use
Tailwind’s arbitrary duration syntax, such as duration-[${duration}ms] or the
existing alert-duration CSS variable, so custom durations are applied correctly.

Source: Linters/SAST tools

Comment on lines +121 to +123
theme.transition?.base,
theme.transition?.duration,
theme.transition?.timing,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep the callback delay aligned with themed duration.

A theme can override the rendered class to duration-700, but the timeout still uses the prop default of 300. onDismiss can unmount the alert before its themed transition finishes. Model the effective duration as one shared numeric value for both CSS and the timer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/Alert/Alert.tsx` around lines 121 - 123, Update
the Alert component’s transition handling to derive one effective numeric
duration from the themed transition value, including overrides such as
duration-700, and reuse that value for both the rendered transition class and
the onDismiss timeout. Replace the independent prop-default timer usage while
preserving existing callback behavior.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/components/Alert/Alert.tsx`:
- Around line 73-81: Update the Alert dismissal flow around the useEffect so the
timeout deadline is captured once when dismissal begins and is not reset when
onDismiss changes during the transition. Store or otherwise stabilize the
clicked dismissal callback/deadline, keep the effect from restarting on parent
rerenders caused by inline callbacks, and add a regression test covering
rerenders during the CSS transition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 88c5f5f4-a063-44b0-a530-dff116d6ebb9

📥 Commits

Reviewing files that changed from the base of the PR and between 2b7fd9c and 437ead4.

📒 Files selected for processing (5)
  • apps/web/content/docs/components/alert.mdx
  • apps/web/examples/alert/alert.transition.tsx
  • apps/web/examples/alert/index.ts
  • packages/ui/src/components/Alert/Alert.test.tsx
  • packages/ui/src/components/Alert/Alert.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/examples/alert/index.ts
  • apps/web/content/docs/components/alert.mdx
  • packages/ui/src/components/Alert/Alert.test.tsx
  • apps/web/examples/alert/alert.transition.tsx

Comment on lines +73 to +81
useEffect(() => {
if (!isDismissing) {
return;
}
const timeoutId = setTimeout(() => {
onDismiss?.(new MouseEvent("click") as unknown as React.MouseEvent<HTMLButtonElement>);
}, duration);
return () => clearTimeout(timeoutId);
}, [isDismissing, duration, onDismiss]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate Alert files =="
fd -a 'Alert\.(tsx|ts|jsx|js)$' . | sed 's#^\./##'

echo "== target file outline/size =="
wc -l packages/ui/src/components/Alert/Alert.tsx 2>/dev/null || true
ast-grep outline packages/ui/src/components/Alert/Alert.tsx 2>/dev/null || true

echo "== target file relevant contents =="
cat -n packages/ui/src/components/Alert/Alert.tsx | sed -n '1,140p'

echo "== usages of Alert =="
rg -n "<Alert|Alert " packages -g '*.{ts,tsx,js,jsx}' | head -120

Repository: themesberg/flowbite-react

Length of output: 8034


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Alert tests relevant contents =="
cat -n packages/ui/src/components/Alert/Alert.test.tsx | sed -n '1,260p'

echo "== package config for jsdom timers/react =="
for f in package.json packages/ui/package.json; do
  if [ -f "$f" ]; then
    echo "-- $f --"
    node -e "const p=require('./$f'); console.log(JSON.stringify({scripts:p.scripts,dependencies:p.dependencies,devDependencies:p.devDependencies}, null, 2))" | sed -n '1,220p'
  fi
done

echo "== focused searches for tests around dismissing/transition/inline callbacks =="
rg -n "onDismiss|dismiss|transition|duration|setInterval|setTimeout|re-render|rerender|render\(\\\(" packages/ui/src/components/Alert packages/ui/src/components -g '!**/*.js' -g '!**/*.map' | sed -n '1,240p'

Repository: themesberg/flowbite-react

Length of output: 13891


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== vitest config =="
fd -a '^(vitest|vite|rollup|rslib)\.(config|configuration)\.(ts|js|mjs|cjs)$' packages/ui | sort | while read -r f; do
  echo "-- $f --"
  cat -n "$f" | sed -n '1,220p'
done

echo "== focused Alert searches =="
rg -n "onDismiss|dismiss|transition|duration|setInterval|setTimeout|re-render|rerender|render\\(" packages/ui/src/components/Alert packages/ui/src/components -g '!**/*.js' -g '!**/*.map' | sed -n '1,260p'

echo "== React dependency versions =="
node - <<'JS'
const fs = require('fs');
for (const f of ['package.json','packages/ui/package.json']) {
  if (!fs.existsSync(f)) continue;
  const p = JSON.parse(fs.readFileSync(f,'utf8'));
  const deps = {...(p.dependencies||{}), ...(p.devDependencies||{})};
  console.log(f, deps.react, deps['react-dom']);
}
JS

Repository: themesberg/flowbite-react

Length of output: 31536


🌐 Web query:

React useEffect cleanup timeout rerender clearTimeout setTimeout dependency changes behavior

💡 Result:

In React, the useEffect cleanup function plays a critical role in managing side effects that require stopping, cancelling, or undoing operations [1][2]. When using setTimeout (or similar async operations like subscriptions or intervals) within useEffect, the cleanup function is essential to ensure that pending operations from a previous render do not interfere with subsequent ones [2][3]. React guarantees the following behavior regarding cleanup execution [2][4]: 1. Timing of Cleanup: React always executes the cleanup function from the previous render before running the effect function for the next render [2][3]. If a component unmounts, the cleanup function is executed one final time [1][2]. 2. Dependency Changes: If you provide a dependency array, React only re-synchronizes the effect (running the cleanup, then the new effect) when one of the values in that array changes [5][4]. If the dependency array is empty ([]), the effect runs only once after the initial mount, and the cleanup runs only when the component unmounts [2][4]. 3. Interaction with setTimeout: When you use setTimeout within an effect, returning clearTimeout(timeoutId) in the cleanup function is the standard pattern to prevent the timeout callback from executing if the component updates or unmounts before the timer expires [2]. Because React cleans up the previous render's effect before starting the next one, this mechanism ensures that at most one timeout is active for that effect at any given time [2]. If you find that your effect is running too often—such as on every render—it is typically because the dependency array is missing or includes values that change on every render [5][6]. You can avoid this by either ensuring the dependencies are stable or by using functional state updates (e.g., setCount(c => c + 1)) to remove the state variable from the effect's dependency list entirely [7][8]. In summary, the cleanup function does not run on every render by default; it only runs when the dependencies change or the component unmounts [2][6]. By cleaning up the previous timeout before establishing a new one, you maintain predictable behavior and avoid race conditions or memory leaks [2][3].

Citations:


Keep the dismissal deadline fixed at click time.

The dismiss effect depends on onDismiss, so an inline dismissal callback causes the timeout to be cleared and restarted on every parent rerender during the CSS transition; a constant callback/deadline should be captured when clicked, with a rerender-during-transition regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/components/Alert/Alert.tsx` around lines 73 - 81, Update the
Alert dismissal flow around the useEffect so the timeout deadline is captured
once when dismissal begins and is not reset when onDismiss changes during the
transition. Store or otherwise stabilize the clicked dismissal
callback/deadline, keep the effect from restarting on parent rerenders caused by
inline callbacks, and add a regression test covering rerenders during the CSS
transition.

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.

Add flowbite missing options in react alert component

1 participant