diff --git a/docs/suspensive.org/src/content/en/docs/react/migrate-to-v3.mdx b/docs/suspensive.org/src/content/en/docs/react/migrate-to-v3.mdx
index 666e493ac..843089c11 100644
--- a/docs/suspensive.org/src/content/en/docs/react/migrate-to-v3.mdx
+++ b/docs/suspensive.org/src/content/en/docs/react/migrate-to-v3.mdx
@@ -1,9 +1,106 @@
+import { Callout } from '@/components'
+
# Migrating to v3
## What's new
-### Draft
+### Now thrown error in `ErrorBoundary` fallback will be passed to parent [#1409](https://github.com/toss/suspensive/pull/1409)
+
+It is not the developer's intention to expose fallbacks recursively due to fallback errors, in v3, errors thrown from fallbacks are caught by the parent ErrorBoundary.
+So this is a new mental model and a BREAKING CHANGE, please understand and use the new behavior.
+
+
+
+Errors thrown from the fallback of AS-IS v2 ErrorBoundary cannot be handled by the parent ErrorBoundary, so they are caught by themselves and fallbacks are exposed recursively.
+
+> This is also the case in [react-error-boundary](https://github.com/bvaughn/react-error-boundary) where fallbacks are exposed recursively as follows.
+
+1. children is exposed
+2. fallback is exposed
+3. fallback is exposed
+4. fallback is exposed
+5. ... recursively exposed fallback is exposed
+
+
+
+Now, errors thrown from the fallback of ErrorBoundary are caught by the parent ErrorBoundary. Therefore, the behavior is as follows.
+
+1. children are exposed
+2. fallback is exposed
+3. This is expected
+
+```tsx /This is expected/ /fallback is exposed/ /children is exposed/
+const Example = () => (
+ <>This is expected>}>
+ (
+
+ fallback is exposed
+
+ )}
+ >
+
+ children is exposed
+
+
+
+)
+
+const Throw = {
+ Error: ({
+ message,
+ after = 0,
+ children,
+ }: PropsWithChildren<{ message: string; after?: number }>) => {
+ const [isNeedThrow, setIsNeedThrow] = useState(after === 0)
+ if (isNeedThrow) {
+ throw new Error(message)
+ }
+ useTimeout(() => setIsNeedThrow(true), after)
+ return <>{children}>
+ },
+}
+```
## Handling BREAKING CHANGES
-### Draft
+### Remove `wrap` & Add `with`
+
+`wrap` has been removed in v3. We added a `with` method to each component that can replace the functionality of wrap.
+
+1. You don't need to understand the builder pattern used in wrap.
+2. Since wrap includes all components internally, the build size increases. In v3, you can import and use only the components you need.
+
+```diff
++ import { ErrorBoundaryGroup, ErrorBoundary, Suspense } from '@suspensive/react'
+- import { wrap } from '@suspensive/react'
+ import { useSuspenseQuery } from '@suspensive/react-query'
+
++ const Example = ErrorBoundaryGroup.with(
++ { blockOutside: false },
++ ErrorBoundary.with(
++ { fallback: ({ error }) => <>{error.message}>, onError: logger.log },
++ Suspense.with({ fallback: <>loading...>, clientOnly: true }, () => {
++ const query = useSuspenseQuery({
++ queryKey: ['key'],
++ queryFn: () => api.text(),
++ })
++ return <>{query.data.text}>
++ })
++ )
++ )
+- const Example = wrap
+- .ErrorBoundaryGroup({ blockOutside: false })
+- .ErrorBoundary({
+- fallback: ({ error }) => <>{error.message}>,
+- onError: logger.log,
+- })
+- .Suspense({ fallback: <>loading...>, clientOnly: true })
+- .on(() => {
+- const query = useSuspenseQuery({
+- queryKey: ['key'],
+- queryFn: () => api.text(),
+- })
+- return <>{query.data.text}>
+- })
+```
diff --git a/docs/suspensive.org/src/content/ko/docs/react/migrate-to-v3.mdx b/docs/suspensive.org/src/content/ko/docs/react/migrate-to-v3.mdx
index 3e0917b6c..647d021c2 100644
--- a/docs/suspensive.org/src/content/ko/docs/react/migrate-to-v3.mdx
+++ b/docs/suspensive.org/src/content/ko/docs/react/migrate-to-v3.mdx
@@ -1,9 +1,106 @@
+import { Callout } from '@/components'
+
# v3로 마이그레이션하기
## 새로운 기능
-### Draft
+### 이제 `ErrorBoundary` fallback에서 thrown error 발생시 부모로 전달됩니다 [#1409](https://github.com/toss/suspensive/pull/1409)
+
+fallback의 에러에 의해 fallback을 재귀적으로 노출하는 것은 개발자가 의도한 것이 아니기 때문에 이를 방지하기 위해 v3에서는 fallback에서 throw된 에러를 부모 ErrorBoundary에 의해 잡히도록 변경되었습니다.
+따라서 이것은 새로운 멘탈모델이자 BREAKING CHANGE이므로 새 동작방식을 이해하고 사용하시기 바랍니다.
+
+
+
+AS-IS v2 ErrorBoundary의 fallback에서 throw된 error는 부모 ErrorBoundary에서 처리할 수 없고 스스로 catch하고 폴백을 재귀적으로 노출되었었습니다
+
+> 이는 [react-error-boundary](https://github.com/bvaughn/react-error-boundary)에서도 아래와 같이 fallback이 재귀적으로 노출되도록 동작합니다.
+
+1. children is exposed
+2. fallback is exposed
+3. fallback is exposed
+4. fallback is exposed
+5. ... 재귀적으로 fallback is exposed을 노출했습니다
+
+
+
+`ErrorBoundary`의 fallback에서 throw된 error는 부모 ErrorBoundary에 의해 잡힙니다. 따라서 아래처럼 동작하게 됩니다.
+
+1. children is exposed
+2. fallback is exposed
+3. This is expected
+
+```tsx /This is expected/ /fallback is exposed/ /children is exposed/
+const Example = () => (
+ <>This is expected>}>
+ (
+
+ fallback is exposed
+
+ )}
+ >
+
+ children is exposed
+
+
+
+)
+
+const Throw = {
+ Error: ({
+ message,
+ after = 0,
+ children,
+ }: PropsWithChildren<{ message: string; after?: number }>) => {
+ const [isNeedThrow, setIsNeedThrow] = useState(after === 0)
+ if (isNeedThrow) {
+ throw new Error(message)
+ }
+ useTimeout(() => setIsNeedThrow(true), after)
+ return <>{children}>
+ },
+}
+```
## BREAKING CHANGES 처리하기
-### Draft
+### `wrap` 제거 & `with` 추가
+
+v3에서 `wrap`을 제거했습니다. wrap의 기능을 대체할 수 있는 `with` 메소드를 각 컴포넌트에 모두 추가해두었습니다.
+
+1. wrap에서 사용하는 builder 패턴을 이해하지 않아도 됩니다.
+2. wrap은 내부적으로 모든 컴포넌트를 포함하고 있기 때문에 빌드 사이즈가 커집니다. v3에서는 필요한 컴포넌트만 import하여 사용할 수 있습니다.
+
+```diff
++ import { ErrorBoundaryGroup, ErrorBoundary, Suspense } from '@suspensive/react'
+- import { wrap } from '@suspensive/react'
+ import { useSuspenseQuery } from '@suspensive/react-query'
+
++ const Example = ErrorBoundaryGroup.with(
++ { blockOutside: false },
++ ErrorBoundary.with(
++ { fallback: ({ error }) => <>{error.message}>, onError: logger.log },
++ Suspense.with({ fallback: <>loading...>, clientOnly: true }, () => {
++ const query = useSuspenseQuery({
++ queryKey: ['key'],
++ queryFn: () => api.text(),
++ })
++ return <>{query.data.text}>
++ })
++ )
++ )
+- const Example = wrap
+- .ErrorBoundaryGroup({ blockOutside: false })
+- .ErrorBoundary({
+- fallback: ({ error }) => <>{error.message}>,
+- onError: logger.log,
+- })
+- .Suspense({ fallback: <>loading...>, clientOnly: true })
+- .on(() => {
+- const query = useSuspenseQuery({
+- queryKey: ['key'],
+- queryFn: () => api.text(),
+- })
+- return <>{query.data.text}>
+- })
+```