diff --git a/packages/tiny-react-hooks/src/index.ts b/packages/tiny-react-hooks/src/index.ts
index d2b6108..d43351e 100644
--- a/packages/tiny-react-hooks/src/index.ts
+++ b/packages/tiny-react-hooks/src/index.ts
@@ -1 +1,2 @@
export * from './useDisclosure';
+export * from './useStableCallback';
diff --git a/packages/tiny-react-hooks/src/useStableCallback/index.ts b/packages/tiny-react-hooks/src/useStableCallback/index.ts
new file mode 100644
index 0000000..9e86eaa
--- /dev/null
+++ b/packages/tiny-react-hooks/src/useStableCallback/index.ts
@@ -0,0 +1 @@
+export * from './useStableCallback';
diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx
new file mode 100644
index 0000000..beef0be
--- /dev/null
+++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx
@@ -0,0 +1,15 @@
+import { useStableCallback } from './useStableCallback';
+
+interface Props {
+ count: number;
+}
+
+export default function Component({
+ count,
+}: Props) {
+ const sum = useStableCallback(() => {
+ console.log(count * 10);
+ });
+
+ return ;
+}
diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md
new file mode 100644
index 0000000..fb35636
--- /dev/null
+++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md
@@ -0,0 +1 @@
+A hook that keep the callback always stable and do not need to add to dependencies of other hooks
\ No newline at end of file
diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts
new file mode 100644
index 0000000..15705d0
--- /dev/null
+++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts
@@ -0,0 +1,27 @@
+import { renderHook } from '@testing-library/react';
+
+import { useStableCallback } from './useStableCallback';
+
+describe('useStableCallback()', () => {
+ it('should return the callback', () => {
+ const mockFn = vi.fn(() => 1);
+ const { result } = renderHook(() => useStableCallback(mockFn));
+
+ expect(result.current()).toBe(1);
+ expect(typeof result.current).toBe('function');
+ });
+
+ describe('when fn is changed', () => {
+ it('should return the correct callback', () => {
+ const mockFn1 = vi.fn(() => 1);
+ const mockFn2 = vi.fn(() => 2);
+ const { result, rerender } = renderHook(
+ ({ fn }) => useStableCallback(fn),
+ { initialProps: { fn: mockFn1 } },
+ );
+ expect(result.current()).toBe(1);
+ rerender({ fn: mockFn2 });
+ expect(result.current()).toBe(2);
+ });
+ });
+});
diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts
new file mode 100644
index 0000000..317c3ab
--- /dev/null
+++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts
@@ -0,0 +1,30 @@
+import { useEffect, useMemo, useRef } from 'react';
+
+/** Hook return type */
+type UseStableCallbackReturnType unknown> = T;
+
+/**
+ * Custom hook that ...
+ * @param {Function} [fn] - your function need to be stable
+ * @returns {UseStableCallbackReturnType} return the the stable callback that will apply the newest function
+ * @public
+ * @example
+ * ```tsx
+ * const stableFn = useStableCallback((a: number, b: number) => {
+ * return a + b;
+ * });
+ *
+ * console.log(stableFn(1, 2)); // 3
+ * ```
+ */
+export function useStableCallback unknown>(
+ fn: T,
+): UseStableCallbackReturnType {
+ const callbackRef = useRef(fn);
+
+ useEffect(() => {
+ callbackRef.current = fn;
+ }, [fn]);
+
+ return useMemo(() => ((...args) => callbackRef.current(...args)) as T, []);
+}