diff --git a/packages/hooks/src/useIndexDBState/README.md b/packages/hooks/src/useIndexDBState/README.md
new file mode 100644
index 0000000000..f348673683
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/README.md
@@ -0,0 +1,92 @@
+# useIndexDBState
+
+A React Hook that stores state into IndexedDB.
+
+## Examples
+
+### Basic usage
+
+```jsx
+import React from 'react';
+import { useIndexDBState } from 'ahooks';
+
+export default function Demo() {
+ const [message, setMessage] = useIndexDBState('message', {
+ defaultValue: 'Hello',
+ });
+
+ return (
+ <>
+ setMessage(e.target.value)}
+ />
+
+ >
+ );
+}
+```
+
+### Store complex object
+
+```jsx
+import React from 'react';
+import { useIndexDBState } from 'ahooks';
+
+export default function Demo() {
+ const [user, setUser] = useIndexDBState('user', {
+ defaultValue: { name: 'Ahooks', age: 1 },
+ });
+
+ return (
+ <>
+
{JSON.stringify(user, null, 2)}
+
+ >
+ );
+}
+```
+
+## API
+
+```typescript
+type SetState = S | ((prevState?: S) => S);
+
+interface Options {
+ defaultValue?: T | (() => T);
+ dbName?: string;
+ storeName?: string;
+ version?: number;
+ onError?: (error: unknown) => void;
+}
+
+const [state, setState] = useIndexDBState(key: string, options?: Options);
+```
+
+### Params
+
+| Property | Description | Type | Default |
+|----------|-------------|------|---------|
+| key | The key of the IndexedDB record | `string` | - |
+| options | Optional configuration | `Options` | - |
+
+### Options
+
+| Property | Description | Type | Default |
+|----------|-------------|------|---------|
+| defaultValue | Default value | `T \| (() => T)` | `undefined` |
+| dbName | Name of the IndexedDB database | `string` | `ahooks-indexdb` |
+| storeName | Name of the object store | `string` | `ahooks-store` |
+| version | Version of the database | `number` | `1` |
+| onError | Error handler | `(error: unknown) => void` | `(e) => console.error(e)` |
+
+### Result
+
+| Property | Description | Type |
+|----------|-------------|------|
+| state | Current state | `T` |
+| setState | Set state | `(value?: SetState) => void` |
\ No newline at end of file
diff --git a/packages/hooks/src/useIndexDBState/__tests__/index.test.ts b/packages/hooks/src/useIndexDBState/__tests__/index.test.ts
new file mode 100644
index 0000000000..1475569601
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/__tests__/index.test.ts
@@ -0,0 +1,116 @@
+import { renderHook, act } from '@testing-library/react';
+import useIndexDBState from '../index';
+
+// 模拟 IndexedDB
+const mockIndexedDB = {
+ open: jest.fn(),
+};
+
+const mockIDBOpenDBRequest = {
+ onerror: null,
+ onsuccess: null,
+ onupgradeneeded: null,
+ result: {
+ transaction: jest.fn(),
+ close: jest.fn(),
+ objectStoreNames: {
+ contains: jest.fn().mockReturnValue(false),
+ },
+ createObjectStore: jest.fn(),
+ },
+};
+
+const mockObjectStore = {
+ get: jest.fn(),
+ put: jest.fn(),
+ delete: jest.fn(),
+};
+
+const mockTransaction = {
+ objectStore: jest.fn().mockReturnValue(mockObjectStore),
+};
+
+// 模拟 window.indexedDB
+Object.defineProperty(window, 'indexedDB', {
+ value: mockIndexedDB,
+ writable: true,
+});
+
+describe('useIndexDBState', () => {
+ beforeEach(() => {
+ // 设置全局 indexedDB 模拟
+ mockIndexedDB.open.mockReturnValue(mockIDBOpenDBRequest);
+ mockIDBOpenDBRequest.result.transaction.mockReturnValue(mockTransaction);
+
+ // 清除之前的模拟调用
+ jest.clearAllMocks();
+ });
+
+ it('should initialize with default value', async () => {
+ const { result } = renderHook(() =>
+ useIndexDBState('test-key', {
+ defaultValue: 'default value',
+ }),
+ );
+
+ expect(result.current[0]).toBe('default value');
+ });
+
+ it('should update state when setState is called', async () => {
+ const { result } = renderHook(() =>
+ useIndexDBState('test-key', {
+ defaultValue: 'default value',
+ }),
+ );
+
+ act(() => {
+ result.current[1]('new value');
+ });
+
+ expect(result.current[0]).toBe('new value');
+ });
+
+ it('should handle function updater', async () => {
+ const { result } = renderHook(() =>
+ useIndexDBState('test-key', {
+ defaultValue: 'default value',
+ }),
+ );
+
+ act(() => {
+ result.current[1]((prev) => `${prev} updated`);
+ });
+
+ expect(result.current[0]).toBe('default value updated');
+ });
+
+ it('should handle undefined value', async () => {
+ const { result } = renderHook(() =>
+ useIndexDBState('test-key', {
+ defaultValue: 'default value',
+ }),
+ );
+
+ act(() => {
+ result.current[1](undefined);
+ });
+
+ expect(result.current[0]).toBeUndefined();
+ });
+
+ it('should call onError when an error occurs', async () => {
+ const onError = jest.fn();
+ mockIndexedDB.open.mockImplementationOnce(() => {
+ throw new Error('Test error');
+ });
+
+ renderHook(() =>
+ useIndexDBState('test-key', {
+ defaultValue: 'default value',
+ onError,
+ }),
+ );
+
+ expect(onError).toHaveBeenCalled();
+ });
+});
\ No newline at end of file
diff --git a/packages/hooks/src/useIndexDBState/demo/demo1.tsx b/packages/hooks/src/useIndexDBState/demo/demo1.tsx
new file mode 100644
index 0000000000..d376e59752
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/demo/demo1.tsx
@@ -0,0 +1,40 @@
+/**
+ * title: Basic usage
+ * desc: Persist state into IndexedDB
+ *
+ * title.zh-CN: 基础用法
+ * desc.zh-CN: 将状态持久化存储到 IndexedDB 中
+ */
+
+import React from 'react';
+import useIndexDBState from '../index';
+
+export default function Demo() {
+ const [message, setMessage] = useIndexDBState('message', {
+ defaultValue: 'Hello',
+ });
+
+ return (
+ <>
+ setMessage(e.target.value)}
+ style={{ width: 200, marginRight: 16 }}
+ />
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/packages/hooks/src/useIndexDBState/demo/demo2.tsx b/packages/hooks/src/useIndexDBState/demo/demo2.tsx
new file mode 100644
index 0000000000..62df7fe128
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/demo/demo2.tsx
@@ -0,0 +1,76 @@
+/**
+ * title: Store complex object
+ * desc: useIndexDBState can store complex objects
+ *
+ * title.zh-CN: 存储复杂对象
+ * desc.zh-CN: useIndexDBState 可以存储复杂对象
+ */
+
+import React, { useState } from 'react';
+import useIndexDBState from '../index';
+
+interface User {
+ name: string;
+ age: number;
+}
+
+export default function Demo() {
+ const [user, setUser] = useIndexDBState('user', {
+ defaultValue: { name: 'Ahooks', age: 1 },
+ });
+
+ const [inputValue, setInputValue] = useState('');
+ const [inputAge, setInputAge] = useState('');
+
+ return (
+ <>
+
+
+ setInputValue(e.target.value)}
+ style={{ width: 200, marginRight: 16 }}
+ />
+
+
+
+ setInputAge(e.target.value)}
+ style={{ width: 200, marginRight: 16 }}
+ />
+
+
+
+
+
+
+
+
{JSON.stringify(user, null, 2)}
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/packages/hooks/src/useIndexDBState/index.en-US.md b/packages/hooks/src/useIndexDBState/index.en-US.md
new file mode 100644
index 0000000000..b99e979c92
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/index.en-US.md
@@ -0,0 +1,58 @@
+---
+nav:
+ path: /hooks
+---
+
+# useIndexDBState
+
+A Hook that stores state into IndexedDB.
+
+## Examples
+
+### Basic usage
+
+
+
+### Complex object
+
+
+
+## API
+
+```typescript
+type SetState = S | ((prevState?: S) => S);
+
+interface Options {
+ defaultValue?: T | (() => T);
+ dbName?: string;
+ storeName?: string;
+ version?: number;
+ onError?: (error: unknown) => void;
+}
+
+const [state, setState] = useIndexDBState(key: string, options?: Options);
+```
+
+### Params
+
+| Property | Description | Type | Default |
+|----------|-------------|------|---------|
+| key | The key of the IndexedDB record | `string` | - |
+| options | Optional configuration | `Options` | - |
+
+### Options
+
+| Property | Description | Type | Default |
+|----------|-------------|------|---------|
+| defaultValue | Default value | `T \| (() => T)` | `undefined` |
+| dbName | Name of the IndexedDB database | `string` | `ahooks-indexdb` |
+| storeName | Name of the object store | `string` | `ahooks-store` |
+| version | Version of the database | `number` | `1` |
+| onError | Error handler | `(error: unknown) => void` | `(e) => console.error(e)` |
+
+### Result
+
+| Property | Description | Type |
+|----------|-------------|------|
+| state | Current state | `T` |
+| setState | Set state | `(value?: SetState) => void` |
\ No newline at end of file
diff --git a/packages/hooks/src/useIndexDBState/index.ts b/packages/hooks/src/useIndexDBState/index.ts
new file mode 100644
index 0000000000..3faf1b8dbb
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/index.ts
@@ -0,0 +1,158 @@
+import { useState, useRef, useEffect, useCallback } from 'react';
+import useMemoizedFn from '../useMemoizedFn';
+import { isFunction } from '../utils';
+import isBrowser from '../utils/isBrowser';
+
+export type SetState = S | ((prevState?: S) => S);
+
+export interface Options {
+ defaultValue?: T | (() => T);
+ dbName?: string;
+ storeName?: string;
+ version?: number;
+ onError?: (error: unknown) => void;
+}
+
+function useIndexDBState(key: string, options: Options = {}) {
+ const {
+ defaultValue,
+ dbName = 'ahooks-indexdb',
+ storeName = 'ahooks-store',
+ version = 1,
+ onError = (e) => {
+ console.error(e);
+ },
+ } = options;
+
+ const [state, setState] = useState(() => {
+ if (isFunction(defaultValue)) {
+ return defaultValue();
+ }
+ return defaultValue;
+ });
+
+ const dbRef = useRef(null);
+ const initialized = useRef(false);
+
+ // 从 IndexDB 获取数据
+ const getValueFromDB = async (k: string): Promise => {
+ return new Promise((resolve, reject) => {
+ if (!dbRef.current) {
+ resolve(undefined);
+ return;
+ }
+
+ try {
+ const transaction = dbRef.current.transaction(storeName, 'readonly');
+ const store = transaction.objectStore(storeName);
+ const request = store.get(k);
+
+ request.onsuccess = () => {
+ resolve(request.result);
+ };
+
+ request.onerror = (event) => {
+ onError(event);
+ reject(event);
+ };
+ } catch (error) {
+ onError(error);
+ reject(error);
+ }
+ });
+ };
+
+ // 更新 IndexDB 中的数据
+ const updateValueInDB = async (k: string, value: T | undefined) => {
+ return new Promise((resolve, reject) => {
+ if (!dbRef.current) {
+ reject(new Error('Database not initialized'));
+ return;
+ }
+
+ try {
+ const transaction = dbRef.current.transaction(storeName, 'readwrite');
+ const store = transaction.objectStore(storeName);
+ let request;
+
+ if (value === undefined) {
+ request = store.delete(k);
+ } else {
+ request = store.put(value, k);
+ }
+
+ request.onsuccess = () => {
+ resolve();
+ };
+
+ request.onerror = (event) => {
+ onError(event);
+ reject(event);
+ };
+ } catch (error) {
+ onError(error);
+ reject(error);
+ }
+ });
+ };
+
+ // 初始化 IndexDB
+ useEffect(() => {
+ if (!isBrowser) {
+ return;
+ }
+
+ const initDB = () => {
+ const request = window.indexedDB.open(dbName, version);
+
+ request.onerror = (event) => {
+ onError(event);
+ };
+
+ request.onsuccess = (event) => {
+ dbRef.current = (event.target as IDBOpenDBRequest).result;
+ // 初始化完成后,尝试获取数据
+ getValueFromDB(key).then((value) => {
+ if (value !== undefined) {
+ setState(value);
+ }
+ initialized.current = true;
+ });
+ };
+
+ request.onupgradeneeded = (event) => {
+ const db = (event.target as IDBOpenDBRequest).result;
+ if (!db.objectStoreNames.contains(storeName)) {
+ db.createObjectStore(storeName);
+ }
+ };
+ };
+
+ if (typeof window !== 'undefined' && window.indexedDB) {
+ initDB();
+ } else {
+ onError(new Error('IndexedDB is not supported in this environment'));
+ }
+
+ return () => {
+ if (dbRef.current) {
+ dbRef.current.close();
+ dbRef.current = null;
+ }
+ };
+ }, [dbName, storeName, version, key, onError]);
+
+ // 更新状态
+ const updateState = useCallback((value?: SetState) => {
+ const currentState = isFunction(value) ? value(state) : value;
+ setState(currentState);
+
+ if (initialized.current && isBrowser) {
+ updateValueInDB(key, currentState).catch(onError);
+ }
+ }, [state, key, onError]);
+
+ return [state, useMemoizedFn(updateState)] as const;
+}
+
+export default useIndexDBState;
\ No newline at end of file
diff --git a/packages/hooks/src/useIndexDBState/index.zh-CN.md b/packages/hooks/src/useIndexDBState/index.zh-CN.md
new file mode 100644
index 0000000000..8366e4caa6
--- /dev/null
+++ b/packages/hooks/src/useIndexDBState/index.zh-CN.md
@@ -0,0 +1,58 @@
+---
+nav:
+ path: /hooks
+---
+
+# useIndexDBState
+
+一个可以将状态持久化存储到 IndexedDB 中的 Hook。
+
+## 代码演示
+
+### 基础用法
+
+
+
+### 存储复杂对象
+
+
+
+## API
+
+```typescript
+type SetState = S | ((prevState?: S) => S);
+
+interface Options {
+ defaultValue?: T | (() => T);
+ dbName?: string;
+ storeName?: string;
+ version?: number;
+ onError?: (error: unknown) => void;
+}
+
+const [state, setState] = useIndexDBState(key: string, options?: Options);
+```
+
+### Params
+
+| 参数 | 说明 | 类型 | 默认值 |
+|---------|-------------------------------|----------|--------|
+| key | 存储在 IndexedDB 中的键名 | `string` | - |
+| options | 可选配置项 | `Options`| - |
+
+### Options
+
+| 参数 | 说明 | 类型 | 默认值 |
+|--------------|-----------------------------------|---------------------------|-------------------------|
+| defaultValue | 默认值 | `T \| (() => T)` | `undefined` |
+| dbName | IndexedDB 数据库名称 | `string` | `ahooks-indexdb` |
+| storeName | 对象存储空间名称 | `string` | `ahooks-store` |
+| version | 数据库版本 | `number` | `1` |
+| onError | 错误处理函数 | `(error: unknown) => void`| `(e) => console.error(e)` |
+
+### Result
+
+| 参数 | 说明 | 类型 |
+|----------|-------------------------|--------------------------------|
+| state | 当前状态 | `T` |
+| setState | 设置状态的函数 | `(value?: SetState) => void`|
\ No newline at end of file