diff --git a/packages/component/package.json b/packages/component/package.json
index 883e6eda8a..f670c4fce6 100644
--- a/packages/component/package.json
+++ b/packages/component/package.json
@@ -10,6 +10,7 @@
"@affine/i18n": "workspace:*",
"@blocksuite/blocks": "0.4.0-20230212194855-047e1b9",
"@blocksuite/editor": "0.4.0-20230212194855-047e1b9",
+ "@blocksuite/global": "0.4.0-20230212194855-047e1b9",
"@blocksuite/icons": "^2.0.2",
"@blocksuite/react": "0.4.0-20230212194855-047e1b9",
"@blocksuite/store": "0.4.0-20230212194855-047e1b9",
diff --git a/packages/component/src/components/BlockSuiteErrorBoundary.tsx b/packages/component/src/components/BlockSuiteErrorBoundary.tsx
new file mode 100644
index 0000000000..6d5cf3cb49
--- /dev/null
+++ b/packages/component/src/components/BlockSuiteErrorBoundary.tsx
@@ -0,0 +1,58 @@
+import React, { Component, ErrorInfo } from 'react';
+import { MigrationError } from '@blocksuite/global/error';
+
+export type BlockSuiteErrorBoundaryProps = React.PropsWithChildren;
+
+type BlockSuiteError = MigrationError | Error;
+
+interface BlockSuiteErrorBoundaryState {
+ error: BlockSuiteError | null;
+}
+
+export class BlockSuiteErrorBoundary extends Component<
+ BlockSuiteErrorBoundaryProps,
+ BlockSuiteErrorBoundaryState
+> {
+ public state: BlockSuiteErrorBoundaryState = {
+ error: null,
+ };
+
+ public static getDerivedStateFromError(
+ error: BlockSuiteError
+ ): BlockSuiteErrorBoundaryState {
+ return { error };
+ }
+
+ public componentDidCatch(error: BlockSuiteError, errorInfo: ErrorInfo) {
+ console.error('Uncaught error:', error, errorInfo);
+ }
+
+ public render() {
+ if (this.state.error) {
+ const isMigrationError = this.state.error instanceof MigrationError;
+
+ return (
+ <>
+
Sorry.. there was an error
+ {isMigrationError ? (
+ <>
+ Migration error
+
+ {' '}
+ Please open a ticket in{' '}
+
+ BlockSuite Github Issue
+
+
+ >
+ ) : null}
+ >
+ );
+ }
+
+ return this.props.children;
+ }
+}
diff --git a/packages/component/src/stories/BlockSuiteErrorBoundary.stories.tsx b/packages/component/src/stories/BlockSuiteErrorBoundary.stories.tsx
new file mode 100644
index 0000000000..892bd30c4f
--- /dev/null
+++ b/packages/component/src/stories/BlockSuiteErrorBoundary.stories.tsx
@@ -0,0 +1,26 @@
+/* deepscan-disable USELESS_ARROW_FUNC_BIND */
+import { Meta, StoryFn } from '@storybook/react';
+import React from 'react';
+import {
+ BlockSuiteErrorBoundary,
+ BlockSuiteErrorBoundaryProps,
+} from '../components/BlockSuiteErrorBoundary';
+import { MigrationError } from '@blocksuite/global/error';
+
+export default {
+ title: 'BlockSuite/ErrorBoundary',
+ component: BlockSuiteErrorBoundary,
+} as Meta;
+
+const Template: StoryFn = args => (
+
+);
+
+export const ErrorComponent = () => {
+ throw new MigrationError('Something incorrect');
+};
+
+export const Primary = Template.bind(undefined);
+Primary.args = {
+ children: [There is no error, ],
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e4d2df86fa..9d0efff66e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -251,6 +251,7 @@ importers:
'@affine/i18n': workspace:*
'@blocksuite/blocks': 0.4.0-20230212194855-047e1b9
'@blocksuite/editor': 0.4.0-20230212194855-047e1b9
+ '@blocksuite/global': 0.4.0-20230212194855-047e1b9
'@blocksuite/icons': ^2.0.2
'@blocksuite/react': 0.4.0-20230212194855-047e1b9
'@blocksuite/store': 0.4.0-20230212194855-047e1b9
@@ -281,6 +282,7 @@ importers:
'@affine/i18n': link:../i18n
'@blocksuite/blocks': 0.4.0-20230212194855-047e1b9_q5owcjtyco3g3ar3k2lch6hule
'@blocksuite/editor': 0.4.0-20230212194855-047e1b9_gnktqkmrh37onc7czor6odaso4
+ '@blocksuite/global': 0.4.0-20230212194855-047e1b9_lit@2.6.1
'@blocksuite/icons': 2.0.4_3stiutgnnbnfnf3uowm5cip22i
'@blocksuite/react': 0.4.0-20230212194855-047e1b9_y5q5lieyb5svaxvsnk5lhlhm4m
'@blocksuite/store': 0.4.0-20230212194855-047e1b9_lit@2.6.1