diff --git a/docs/content/react/components/snackbar.mdx b/docs/content/react/components/snackbar.mdx
index 782c13210a..4923b525c8 100644
--- a/docs/content/react/components/snackbar.mdx
+++ b/docs/content/react/components/snackbar.mdx
@@ -83,6 +83,22 @@ npx @seed-design/cli@latest add ui:snackbar
```
+### Strategy
+
+Snackbar가 이미 표시 중일 때 새로운 Snackbar를 생성하면, 기본적으로 기존 Snackbar를 즉시 교체합니다 (`immediate`).
+큐에 넣고 순차적으로 보여주려면 `strategy: "queued"`를 사용합니다.
+
+`strategy`는 `SnackbarProvider`에서 기본값을 설정하거나, `create()` 호출 시 개별적으로 지정할 수 있습니다.
+
+
+ ```json doc-gen:file
+ {
+ "file": "examples/react/snackbar/strategy.tsx",
+ "codeblock": true
+ }
+ ```
+
+
### Avoid Overlap
`` 컴포넌트를 사용하여 스낵바가 겹치지 않아야 하는 영역을 지정할 수 있습니다.
diff --git a/docs/examples/react/snackbar/strategy.tsx b/docs/examples/react/snackbar/strategy.tsx
new file mode 100644
index 0000000000..330e512283
--- /dev/null
+++ b/docs/examples/react/snackbar/strategy.tsx
@@ -0,0 +1,40 @@
+import { ActionButton } from "seed-design/ui/action-button";
+import { Snackbar, SnackbarProvider, useSnackbarAdapter } from "seed-design/ui/snackbar";
+
+function Component() {
+ const adapter = useSnackbarAdapter();
+
+ return (
+
+
+ adapter.create({
+ render: () => ,
+ })
+ }
+ >
+ Immediate (positive)
+
+
+ adapter.create({
+ strategy: "queued",
+ render: () => ,
+ })
+ }
+ >
+ Queued (critical)
+
+
+ );
+}
+
+export default function SnackbarStrategy() {
+ return (
+
+
+
+ );
+}
diff --git a/examples/stackflow-spa/src/activities/ActivityHome.tsx b/examples/stackflow-spa/src/activities/ActivityHome.tsx
index 238cc96648..2b5a1d57e2 100644
--- a/examples/stackflow-spa/src/activities/ActivityHome.tsx
+++ b/examples/stackflow-spa/src/activities/ActivityHome.tsx
@@ -220,6 +220,28 @@ const ActivityHome: StaticActivityComponentType<"ActivityHome"> = ({ params }) =
),
}),
},
+ {
+ title: "Snackbar (queued)",
+ onClick: () =>
+ snackbarAdapter.create({
+ strategy: "queued",
+ render: () => ,
+ }),
+ },
+ {
+ // 기존 스낵바를 먼저 닫고 다음 tick에 새 스낵바를 띄우는 패턴.
+ // dismiss 상태 전이가 적용된 뒤에 create가 실행되므로
+ // 항상 새 스낵바부터 활성화되는 것을 보장한다.
+ title: "Snackbar (dismiss+setTimeout workaround)",
+ onClick: () => {
+ snackbarAdapter.dismiss();
+ setTimeout(() => {
+ snackbarAdapter.create({
+ render: () => ,
+ });
+ }, 0);
+ },
+ },
],
},
{
diff --git a/packages/react-headless/snackbar/src/Snackbar.tsx b/packages/react-headless/snackbar/src/Snackbar.tsx
index cd88aa9a82..935ea3e4c9 100644
--- a/packages/react-headless/snackbar/src/Snackbar.tsx
+++ b/packages/react-headless/snackbar/src/Snackbar.tsx
@@ -12,11 +12,8 @@ export interface SnackbarRootProviderProps extends UseSnackbarProps {
children: React.ReactNode;
}
-export const SnackbarRootProvider = ({
- children,
- pauseOnInteraction,
-}: SnackbarRootProviderProps) => {
- const api = useSnackbar({ pauseOnInteraction });
+export const SnackbarRootProvider = ({ children, ...props }: SnackbarRootProviderProps) => {
+ const api = useSnackbar(props);
return {children};
};
diff --git a/packages/react-headless/snackbar/src/useSnackbar.test.tsx b/packages/react-headless/snackbar/src/useSnackbar.test.tsx
new file mode 100644
index 0000000000..cf3b20d11f
--- /dev/null
+++ b/packages/react-headless/snackbar/src/useSnackbar.test.tsx
@@ -0,0 +1,372 @@
+import { act, render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { afterAll, beforeEach, describe, expect, it, jest, mock } from "bun:test";
+import { StrictMode } from "react";
+
+import {
+ SnackbarRegion,
+ SnackbarRenderer,
+ SnackbarRoot,
+ SnackbarRootProvider,
+ type SnackbarRootProviderProps,
+} from "./Snackbar";
+import { useSnackbarContext, type UseSnackbarContext } from "./useSnackbarContext";
+
+class ResizeObserver {
+ observe() {}
+ unobserve() {}
+ disconnect() {}
+}
+
+const originalResizeObserver = window.ResizeObserver;
+window.ResizeObserver = ResizeObserver;
+
+afterAll(() => {
+ window.ResizeObserver = originalResizeObserver;
+});
+
+let snackbarApi: UseSnackbarContext;
+
+function SnackbarControls() {
+ const api = useSnackbarContext();
+ snackbarApi = api;
+
+ return (
+
+
+ {api.currentSnackbar && (
+
+
+
+ )}
+
+
+ );
+}
+
+function setUp(providerProps: Omit = {}) {
+ return {
+ user: userEvent.setup({ advanceTimers: (ms) => jest.advanceTimersByTime(ms) }),
+ ...render(
+
+
+ ,
+ ),
+ };
+}
+
+function setUpStrict(providerProps: Omit = {}) {
+ return {
+ user: userEvent.setup({ advanceTimers: (ms) => jest.advanceTimersByTime(ms) }),
+ ...render(
+
+
+
+
+ ,
+ ),
+ };
+}
+
+function createSnackbar(message: string, options: Record = {}) {
+ return {
+ render: () => {message},
+ timeout: 5000,
+ removeDelay: 200,
+ ...options,
+ };
+}
+
+describe("useSnackbar", () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ describe("common", () => {
+ it("should display a single snackbar", () => {
+ setUp();
+
+ act(() => {
+ snackbarApi.create(createSnackbar("Hello"));
+ });
+
+ expect(screen.getByText("Hello")).toBeInTheDocument();
+ });
+
+ it("should auto-dismiss after timeout", () => {
+ setUp();
+
+ act(() => {
+ snackbarApi.create(createSnackbar("Auto dismiss", { timeout: 1000, removeDelay: 100 }));
+ });
+
+ expect(screen.getByText("Auto dismiss")).toBeInTheDocument();
+
+ // timeout fires → dismissing state
+ act(() => jest.advanceTimersByTime(1000));
+ // removeDelay fires → inactive state
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.queryByText("Auto dismiss")).not.toBeInTheDocument();
+ });
+
+ it("should dismiss on dismiss() call", () => {
+ setUp();
+
+ act(() => {
+ snackbarApi.create(createSnackbar("Dismiss me", { removeDelay: 100 }));
+ });
+
+ expect(screen.getByText("Dismiss me")).toBeInTheDocument();
+
+ act(() => snackbarApi.dismiss());
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.queryByText("Dismiss me")).not.toBeInTheDocument();
+ });
+
+ it("should call onClose exactly once when dismiss() is invoked", () => {
+ const onClose = mock(() => {});
+ setUp();
+
+ act(() => {
+ snackbarApi.create(createSnackbar("Bye", { onClose, removeDelay: 100 }));
+ });
+
+ act(() => snackbarApi.dismiss());
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(onClose).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe("StrictMode safety", () => {
+ it("should not lose queued items under StrictMode double-invocation", () => {
+ setUpStrict({ strategy: "queued" });
+
+ act(() => {
+ snackbarApi.create(createSnackbar("A", { timeout: 10000, removeDelay: 1000 }));
+ snackbarApi.create(createSnackbar("B", { timeout: 10000, removeDelay: 1000 }));
+ snackbarApi.create(createSnackbar("C", { timeout: 10000, removeDelay: 1000 }));
+ });
+
+ expect(screen.getByText("A")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(2);
+ });
+ });
+
+ describe("immediate (default)", () => {
+ it("should replace current snackbar with new one", () => {
+ setUp();
+
+ act(() => snackbarApi.create(createSnackbar("First", { removeDelay: 100 })));
+ expect(screen.getByText("First")).toBeInTheDocument();
+
+ act(() => snackbarApi.create(createSnackbar("Second")));
+
+ // First is in dismissing state (exit animation)
+ // Advance removeDelay to complete exit, then new one enters
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.queryByText("First")).not.toBeInTheDocument();
+ expect(screen.getByText("Second")).toBeInTheDocument();
+ });
+
+ it("should call onClose of replaced snackbar", () => {
+ const onClose = mock(() => {});
+ setUp();
+
+ act(() => snackbarApi.create(createSnackbar("First", { onClose, removeDelay: 100 })));
+
+ act(() => snackbarApi.create(createSnackbar("Second")));
+
+ // onClose fires during dismissing → inactive transition
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(onClose).toHaveBeenCalledTimes(1);
+ });
+
+ it("should clear queue on replacement", () => {
+ setUp({ strategy: "queued" });
+
+ // Queue up 3 snackbars in queued mode
+ act(() => snackbarApi.create(createSnackbar("First", { removeDelay: 100 })));
+ act(() => {
+ snackbarApi.create(createSnackbar("Queued A", { strategy: "queued" }));
+ snackbarApi.create(createSnackbar("Queued B", { strategy: "queued" }));
+ });
+
+ expect(screen.getByText("First")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(2);
+
+ // Immediate snackbar clears queue, triggers dismiss of First
+ act(() => snackbarApi.create(createSnackbar("Urgent", { strategy: "immediate" })));
+
+ // Advance removeDelay for First to exit, then Urgent enters
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.getByText("Urgent")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(0);
+ });
+
+ it("should not call onClose of queued snackbars that were dropped without being shown", () => {
+ const onCloseFirst = mock(() => {});
+ const onCloseQueuedA = mock(() => {});
+ const onCloseQueuedB = mock(() => {});
+ setUp({ strategy: "queued" });
+
+ act(() =>
+ snackbarApi.create(createSnackbar("First", { onClose: onCloseFirst, removeDelay: 100 })),
+ );
+ act(() => {
+ snackbarApi.create(
+ createSnackbar("Queued A", { strategy: "queued", onClose: onCloseQueuedA }),
+ );
+ snackbarApi.create(
+ createSnackbar("Queued B", { strategy: "queued", onClose: onCloseQueuedB }),
+ );
+ });
+
+ expect(snackbarApi.queue.length).toBe(2);
+
+ // Immediate snackbar drops the queue (Queued A, B were never shown)
+ act(() => snackbarApi.create(createSnackbar("Urgent", { strategy: "immediate" })));
+ act(() => jest.advanceTimersByTime(100));
+
+ // First was shown → its onClose fires once
+ expect(onCloseFirst).toHaveBeenCalledTimes(1);
+ // Queued A, B never became currentSnackbar → their onClose must not fire
+ expect(onCloseQueuedA).not.toHaveBeenCalled();
+ expect(onCloseQueuedB).not.toHaveBeenCalled();
+ });
+
+ it("should restart timeout after replacement", () => {
+ setUp();
+
+ act(() => snackbarApi.create(createSnackbar("First", { timeout: 1000, removeDelay: 100 })));
+
+ // Advance 800ms (not yet dismissed)
+ act(() => jest.advanceTimersByTime(800));
+ expect(screen.getByText("First")).toBeInTheDocument();
+
+ // Replace: triggers dismiss of First
+ act(() => snackbarApi.create(createSnackbar("Second", { timeout: 1000, removeDelay: 100 })));
+
+ // Advance 100ms removeDelay for First exit → Second enters with fresh timeout
+ act(() => jest.advanceTimersByTime(100));
+ expect(screen.getByText("Second")).toBeInTheDocument();
+
+ // Advance 800ms — Second should still be visible (fresh timeout = 1000ms)
+ act(() => jest.advanceTimersByTime(800));
+ expect(screen.getByText("Second")).toBeInTheDocument();
+
+ // Advance remaining 200ms → dismissing, then 100ms removeDelay → gone
+ act(() => jest.advanceTimersByTime(200));
+ act(() => jest.advanceTimersByTime(100));
+ expect(screen.queryByText("Second")).not.toBeInTheDocument();
+ });
+ });
+
+ describe("queued", () => {
+ it("should queue snackbars when strategy is queued", () => {
+ setUp({ strategy: "queued" });
+
+ act(() => {
+ snackbarApi.create(createSnackbar("First", { timeout: 1000, removeDelay: 100 }));
+ snackbarApi.create(createSnackbar("Second"));
+ });
+
+ // First is showing, Second is in queue
+ expect(screen.getByText("First")).toBeInTheDocument();
+ expect(screen.queryByText("Second")).not.toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(1);
+
+ // Dismiss first → second shows
+ act(() => jest.advanceTimersByTime(1000));
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.queryByText("First")).not.toBeInTheDocument();
+ expect(screen.getByText("Second")).toBeInTheDocument();
+ });
+ });
+
+ describe("per-snackbar strategy override", () => {
+ it("should mix queued and immediate overrides in immediate provider", () => {
+ setUp(); // default: immediate
+
+ // 1. First shows, then Queued-A and Queued-B are created in same act
+ act(() => {
+ snackbarApi.create(createSnackbar("First", { timeout: 1000, removeDelay: 100 }));
+ snackbarApi.create(
+ createSnackbar("Queued-A", { strategy: "queued", timeout: 1000, removeDelay: 100 }),
+ );
+ snackbarApi.create(
+ createSnackbar("Queued-B", { strategy: "queued", timeout: 1000, removeDelay: 100 }),
+ );
+ });
+
+ expect(screen.getByText("First")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(2);
+
+ // 4. Urgent replaces First: dismiss First, queue only Urgent
+ act(() => snackbarApi.create(createSnackbar("Urgent", { timeout: 1000, removeDelay: 100 })));
+
+ // First exit animation
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.queryByText("First")).not.toBeInTheDocument();
+ expect(screen.getByText("Urgent")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(0);
+
+ // 5. After Urgent times out, nothing left
+ act(() => jest.advanceTimersByTime(1000));
+ act(() => jest.advanceTimersByTime(100));
+ expect(screen.queryByText("Urgent")).not.toBeInTheDocument();
+ });
+
+ it("should mix queued and immediate overrides in queued provider", () => {
+ setUp({ strategy: "queued" }); // default: queued
+
+ // 1. First shows, Second and Third queue — all in same act
+ act(() => {
+ snackbarApi.create(createSnackbar("First", { timeout: 1000, removeDelay: 100 }));
+ snackbarApi.create(createSnackbar("Second", { timeout: 1000, removeDelay: 100 }));
+ snackbarApi.create(createSnackbar("Third", { timeout: 1000, removeDelay: 100 }));
+ });
+
+ expect(screen.getByText("First")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(2);
+
+ // 4. Urgent with immediate override: dismiss First, queue only Urgent
+ act(() =>
+ snackbarApi.create(
+ createSnackbar("Urgent", { strategy: "immediate", timeout: 1000, removeDelay: 100 }),
+ ),
+ );
+
+ // First exit animation
+ act(() => jest.advanceTimersByTime(100));
+
+ expect(screen.queryByText("First")).not.toBeInTheDocument();
+ expect(screen.getByText("Urgent")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(0);
+
+ // 5. While Urgent is showing, queue a new one (default queued)
+ act(() =>
+ snackbarApi.create(createSnackbar("After-Urgent", { timeout: 1000, removeDelay: 100 })),
+ );
+ expect(screen.getByText("Urgent")).toBeInTheDocument();
+ expect(snackbarApi.queue.length).toBe(1);
+
+ // 6. Urgent times out → After-Urgent shows from queue
+ act(() => jest.advanceTimersByTime(1000));
+ act(() => jest.advanceTimersByTime(100));
+ expect(screen.queryByText("Urgent")).not.toBeInTheDocument();
+ expect(screen.getByText("After-Urgent")).toBeInTheDocument();
+ });
+ });
+});
diff --git a/packages/react-headless/snackbar/src/useSnackbar.ts b/packages/react-headless/snackbar/src/useSnackbar.ts
index 219a2eeb7f..8f159bbf9c 100644
--- a/packages/react-headless/snackbar/src/useSnackbar.ts
+++ b/packages/react-headless/snackbar/src/useSnackbar.ts
@@ -1,6 +1,6 @@
import { ariaAttr, buttonProps, dataAttr, elementProps } from "@seed-design/dom-utils";
import { useSupports } from "@seed-design/react-supports";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useEffect, useMemo, useReducer } from "react";
import { useSafeOffset } from "./useSafeOffset";
type SnackbarState = "inactive" | "active" | "persist" | "dismissing";
@@ -11,6 +11,14 @@ interface UseSnackbarStateProps {
* @default true
*/
pauseOnInteraction?: boolean;
+
+ /**
+ * How to handle multiple snackbars.
+ * - `"immediate"`: New snackbar replaces the current one instantly.
+ * - `"queued"`: New snackbar waits in a queue until the current one is dismissed.
+ * @default "immediate"
+ */
+ strategy?: "immediate" | "queued";
}
export interface CreateSnackbarOptions {
@@ -34,95 +42,110 @@ export interface CreateSnackbarOptions {
* The content to render in the snackbar region
*/
render: () => React.ReactNode;
+
+ /**
+ * Override the provider-level strategy for this specific snackbar.
+ * - `"immediate"`: Replace the current snackbar instantly.
+ * - `"queued"`: Wait in the queue until the current one is dismissed.
+ */
+ strategy?: "immediate" | "queued";
}
-function useSnackbarState({ pauseOnInteraction = true }: UseSnackbarStateProps) {
- const [state, setState] = useState("inactive");
- const [queue, setQueue] = useState([]);
- const [currentSnackbar, setCurrentSnackbar] = useState(null);
+interface ReducerState {
+ state: SnackbarState;
+ queue: CreateSnackbarOptions[];
+ currentSnackbar: CreateSnackbarOptions | null;
+}
- const visibleDuration = currentSnackbar?.timeout ?? 4000;
- const removeDelay = currentSnackbar?.removeDelay ?? 200;
- const visible = state === "active" || state === "persist";
+type ReducerAction =
+ | { type: "PUSH"; option: CreateSnackbarOptions; strategy: "immediate" | "queued" }
+ | { type: "ACTIVATE_NEXT" }
+ | { type: "PAUSE"; pauseOnInteraction: boolean }
+ | { type: "RESUME" }
+ | { type: "DISMISS" }
+ | { type: "REMOVE" };
- // actions
- const push = useCallback((option: CreateSnackbarOptions) => {
- setQueue((prev) => [...prev, option]);
- }, []);
-
- const pop = useCallback(() => {
- setQueue(([snackbar, ...rest]) => {
- setCurrentSnackbar(snackbar ?? null);
- return rest;
- });
- }, []);
-
- const removeCurrentSnackbar = useCallback(() => {
- setCurrentSnackbar(null);
- }, []);
-
- const invokeOnClose = useCallback(() => {
- if (currentSnackbar?.onClose) {
- currentSnackbar.onClose();
- }
- }, [currentSnackbar]);
+const initialState: ReducerState = {
+ state: "inactive",
+ queue: [],
+ currentSnackbar: null,
+};
- // entry events
- useEffect(() => {
- if (state === "inactive") {
- if (queue.length >= 1) {
- pop();
- setState("active");
+function reducer(s: ReducerState, action: ReducerAction): ReducerState {
+ switch (action.type) {
+ case "PUSH": {
+ const { option, strategy } = action;
+ if (strategy === "immediate") {
+ switch (s.state) {
+ case "inactive":
+ return { state: "active", currentSnackbar: option, queue: [] };
+ case "dismissing":
+ return { ...s, queue: [option] };
+ default:
+ return { ...s, state: "dismissing", queue: [option] };
+ }
}
+ if (s.state === "inactive") {
+ return { state: "active", currentSnackbar: option, queue: [] };
+ }
+ return { ...s, queue: [...s.queue, option] };
}
-
- if (state === "active") {
- const timeout = setTimeout(() => {
- setState("dismissing");
- }, visibleDuration);
- return () => clearTimeout(timeout);
+ case "ACTIVATE_NEXT": {
+ if (s.state !== "inactive" || s.queue.length === 0) return s;
+ const [first, ...rest] = s.queue;
+ return { state: "active", currentSnackbar: first, queue: rest };
}
+ case "PAUSE":
+ return action.pauseOnInteraction && s.state === "active" ? { ...s, state: "persist" } : s;
+ case "RESUME":
+ return s.state === "persist" ? { ...s, state: "active" } : s;
+ case "DISMISS":
+ return s.state === "active" || s.state === "persist" ? { ...s, state: "dismissing" } : s;
+ case "REMOVE":
+ return { ...s, state: "inactive", currentSnackbar: null };
+ }
+}
+
+function useSnackbarState({
+ pauseOnInteraction = true,
+ strategy = "immediate",
+}: UseSnackbarStateProps) {
+ const [{ state, queue, currentSnackbar }, dispatch] = useReducer(reducer, initialState);
- if (state === "dismissing") {
- const timeout = setTimeout(() => {
- setState("inactive");
- invokeOnClose();
- removeCurrentSnackbar();
- }, removeDelay);
- return () => clearTimeout(timeout);
+ const visibleDuration = currentSnackbar?.timeout ?? 4000;
+ const removeDelay = currentSnackbar?.removeDelay ?? 200;
+ const visible = state === "active" || state === "persist";
+
+ useEffect(() => {
+ switch (state) {
+ case "inactive": {
+ if (queue.length >= 1) dispatch({ type: "ACTIVATE_NEXT" });
+ break;
+ }
+ case "active": {
+ const timeout = setTimeout(() => dispatch({ type: "DISMISS" }), visibleDuration);
+ return () => clearTimeout(timeout);
+ }
+ case "dismissing": {
+ const timeout = setTimeout(() => {
+ currentSnackbar?.onClose?.();
+ dispatch({ type: "REMOVE" });
+ }, removeDelay);
+ return () => clearTimeout(timeout);
+ }
}
- }, [state, queue, visibleDuration, removeDelay, pop, invokeOnClose, removeCurrentSnackbar]);
+ }, [state, queue.length, currentSnackbar, visibleDuration, removeDelay]);
- // events
const events = useMemo(
() => ({
push: (option: CreateSnackbarOptions) => {
- push(option);
- if (state === "inactive") {
- pop();
- setState("active");
- }
- },
- pause: () => {
- if (state === "active") {
- if (pauseOnInteraction) {
- setState("persist");
- }
- }
- },
- resume: () => {
- if (state === "persist") {
- setState("active");
- }
- },
- dismiss: () => {
- if (state === "active" || state === "persist") {
- setState("dismissing");
- invokeOnClose();
- }
+ dispatch({ type: "PUSH", option, strategy: option.strategy ?? strategy });
},
+ pause: () => dispatch({ type: "PAUSE", pauseOnInteraction }),
+ resume: () => dispatch({ type: "RESUME" }),
+ dismiss: () => dispatch({ type: "DISMISS" }),
}),
- [state, push, pop, invokeOnClose, pauseOnInteraction],
+ [strategy, pauseOnInteraction],
);
return useMemo(
diff --git a/packages/react/src/components/Snackbar/useSnackbarAdapter.ts b/packages/react/src/components/Snackbar/useSnackbarAdapter.ts
index dd17c7b33b..2dde29ccd3 100644
--- a/packages/react/src/components/Snackbar/useSnackbarAdapter.ts
+++ b/packages/react/src/components/Snackbar/useSnackbarAdapter.ts
@@ -17,6 +17,7 @@ export function useSnackbarAdapter() {
removeDelay: options.removeDelay ?? 200,
onClose: options.onClose,
render: options.render,
+ strategy: options.strategy,
});
},
dismiss: api.dismiss,