From a63ce3405ac207d296fbf534cc73d4043f260528 Mon Sep 17 00:00:00 2001 From: q404365631 <398291278@qq.com> Date: Thu, 4 Jun 2026 08:54:13 +0800 Subject: [PATCH 1/2] feat(ui): add React error boundary to Layout to recover from render errors --- components/ErrorBoundary.tsx | 54 ++++++++++++++++++++++++++++++++++++ components/layout/Layout.tsx | 33 ++++++++++++++++------ 2 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 components/ErrorBoundary.tsx diff --git a/components/ErrorBoundary.tsx b/components/ErrorBoundary.tsx new file mode 100644 index 000000000000..c40515f45e6a --- /dev/null +++ b/components/ErrorBoundary.tsx @@ -0,0 +1,54 @@ +import { Component, ErrorInfo, ReactNode } from 'react'; + +interface ErrorBoundaryProps { + children: ReactNode; + fallback?: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +/** + * Top-level ErrorBoundary for the AsyncAPI website. + * + * Wrapping the whole app in at the layout level means a render + * error in any descendant component (e.g. a future page, a broken MDX block) + * no longer tears down the whole site — it just shows a small recoverable + * fallback so navigation still works. + * + * See: https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary + */ +export default class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, info: ErrorInfo): void { + // Surface the error to the browser console; in production a real Sentry + // hook would go here. + // eslint-disable-next-line no-console + console.error('[ErrorBoundary] Caught a render error:', error, info); + } + + render(): ReactNode { + if (this.state.hasError) { + if (this.props.fallback) { + return this.props.fallback; + } + return ( +
+

Something went wrong.

+

Please refresh the page, or use the navigation to continue.

+
+ ); + } + return this.props.children; + } +} diff --git a/components/layout/Layout.tsx b/components/layout/Layout.tsx index f7ed8782d7c5..4b1a9d1d574b 100644 --- a/components/layout/Layout.tsx +++ b/components/layout/Layout.tsx @@ -6,6 +6,7 @@ import type { IPost, IPosts } from '@/types/post'; import BlogContext from '../../context/BlogContext'; import { getAllPosts, getDocBySlug, getPostBySlug } from '../../utils/api'; +import ErrorBoundary from '../ErrorBoundary'; import BlogLayout from './BlogLayout'; import DocsLayout from './DocsLayout'; import GenericPostLayout from './GenericPostLayout'; @@ -28,9 +29,11 @@ export default function Layout({ children }: ILayoutProps): React.JSX.Element { return (
- - {children} - + + + {children} + +
); } @@ -39,24 +42,36 @@ export default function Layout({ children }: ILayoutProps): React.JSX.Element { return (
- - {children} - + + + {children} + +
); } if (pathname === '/blog') { return (
- {children} + + {children} +
); } const post = getPostBySlug(pathname); if (post) { - return {children}; + return ( + + {children} + + ); } - return
{children}
; + return ( +
+ {children} +
+ ); } From 98767d4432a793525fc4192ae07c733b964c87d1 Mon Sep 17 00:00:00 2001 From: q404365631 <398291278@qq.com> Date: Fri, 5 Jun 2026 00:27:26 +0800 Subject: [PATCH 2/2] chore: add changeset for #5539 --- .changeset/feat-error-boundaries-5047.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/feat-error-boundaries-5047.md diff --git a/.changeset/feat-error-boundaries-5047.md b/.changeset/feat-error-boundaries-5047.md new file mode 100644 index 000000000000..a60ce37a940f --- /dev/null +++ b/.changeset/feat-error-boundaries-5047.md @@ -0,0 +1,5 @@ +--- +'asyncapi-website': patch +--- + +feat(ui): add a top-level React `ErrorBoundary` and wrap every `Layout` return tree in it, so a render error in any descendant component (broken MDX page, future page, etc.) no longer tears down the whole site — it shows a recoverable fallback instead.