diff --git a/apps/ssr-tests-v9/package.json b/apps/ssr-tests-v9/package.json
index 27896f18d76f2..bf55eab6a4bae 100644
--- a/apps/ssr-tests-v9/package.json
+++ b/apps/ssr-tests-v9/package.json
@@ -18,7 +18,8 @@
"test-ssr": "test-ssr \"./src/stories/**/*.stories.tsx\""
},
"dependencies": {
- "@fluentui/react-components": "*"
+ "@fluentui/react-components": "*",
+ "@fluentui/react-utilities": "*"
},
"devDependencies": {
"@fluentui/eslint-plugin": "*",
diff --git a/apps/ssr-tests-v9/src/stories/Utilitites/index.stories.tsx b/apps/ssr-tests-v9/src/stories/Utilitites/index.stories.tsx
new file mode 100644
index 0000000000000..d06c147c86884
--- /dev/null
+++ b/apps/ssr-tests-v9/src/stories/Utilitites/index.stories.tsx
@@ -0,0 +1,5 @@
+export { Default } from './useAnimationFrame.stories';
+
+export default {
+ title: 'Utilities/useAnimationFrame',
+};
diff --git a/apps/ssr-tests-v9/src/stories/Utilitites/useAnimationFrame.stories.tsx b/apps/ssr-tests-v9/src/stories/Utilitites/useAnimationFrame.stories.tsx
new file mode 100644
index 0000000000000..aba7f94b251a8
--- /dev/null
+++ b/apps/ssr-tests-v9/src/stories/Utilitites/useAnimationFrame.stories.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import { useAnimationFrame } from '@fluentui/react-utilities';
+
+export const Default = () => {
+ const [setAnimationFrame, clearAnimationFrame] = useAnimationFrame();
+ const [visible, setVisible] = React.useState(false);
+
+ React.useEffect(() => {
+ setAnimationFrame(() => setVisible(true));
+
+ return () => clearAnimationFrame();
+ }, [setAnimationFrame]);
+
+ return visible ?
Test the renderization
: null;
+};
diff --git a/change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json b/change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json
new file mode 100644
index 0000000000000..0b0617f983224
--- /dev/null
+++ b/change/@fluentui-react-utilities-b4af8469-27c7-4b29-a6a9-13ef542e6779.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "fix: use timeout instead of requestAnimationFrame for ssr",
+ "packageName": "@fluentui/react-utilities",
+ "email": "marcosvmmoura@gmail.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts
index f2453bd545a4d..a95e862aad739 100644
--- a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts
+++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts
@@ -1,5 +1,12 @@
+import { canUseDOM } from '../ssr/canUseDOM';
import { useBrowserTimer } from './useBrowserTimer';
+const setAnimationFrameNoop = (callback: FrameRequestCallback) => {
+ callback(0);
+ return 0;
+};
+const cancelAnimationFrameNoop = (handle: number) => handle;
+
/**
* @internal
* Helper to manage a browser requestAnimationFrame.
@@ -9,6 +16,11 @@ import { useBrowserTimer } from './useBrowserTimer';
* @returns A pair of [requestAnimationFrame, cancelAnimationFrame] that are stable between renders.
*/
export function useAnimationFrame() {
+ const isDOM = canUseDOM();
+
// TODO: figure it out a way to not call global.requestAnimationFrame and instead infer window from some context
- return useBrowserTimer(requestAnimationFrame, cancelAnimationFrame);
+ const setAnimationFrame = isDOM ? requestAnimationFrame : setAnimationFrameNoop;
+ const clearAnimationFrame = isDOM ? cancelAnimationFrame : cancelAnimationFrameNoop;
+
+ return useBrowserTimer(setAnimationFrame, clearAnimationFrame);
}