+ ${choose(this.state, [
+ [
+ 'loading',
+ () =>
+ html`
+ Rendering Typst code...
+
`,
+ ],
+ [
+ 'error',
+ () =>
+ html`
+ ${this._errorMessageComponent}
+
`,
+ ],
+ [
+ 'syntax-error',
+ () =>
+ html`
+ Typst code has errors: ${this.errorMessage ?? 'Unknown error.'}
+ ${this._errorMessageDetail}
+
`,
+ ],
+ [
+ 'fallback',
+ () =>
+ html`
+ Enter Typst code to preview.
+
`,
+ ],
+ ])}
+
+ ${this._finalPreview}
+
+
+ `;
+ }
+}
+
+export function effects() {
+ customElements.define('typst-preview', TypstPreview);
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'typst-preview': TypstPreview;
+ }
+}
diff --git a/packages/frontend/core/src/blocksuite/view-extensions/code-block-preview/typst.ts b/packages/frontend/core/src/blocksuite/view-extensions/code-block-preview/typst.ts
new file mode 100644
index 000000000..d48594d84
--- /dev/null
+++ b/packages/frontend/core/src/blocksuite/view-extensions/code-block-preview/typst.ts
@@ -0,0 +1,57 @@
+import { $typst, type BeforeBuildFn, loadFonts } from '@myriaddreamin/typst.ts';
+
+const FONT_CDN_URLS = [
+ 'https://cdn.affine.pro/fonts/Inter-Regular.woff',
+ 'https://cdn.affine.pro/fonts/Inter-SemiBold.woff',
+ 'https://cdn.affine.pro/fonts/Inter-Italic.woff',
+ 'https://cdn.affine.pro/fonts/Inter-SemiBoldItalic.woff',
+ 'https://cdn.affine.pro/fonts/SarasaGothicCL-Regular.ttf',
+] as const;
+
+const getBeforeBuildHooks = (): BeforeBuildFn[] => [
+ loadFonts([...FONT_CDN_URLS]),
+];
+
+const compilerWasmUrl = new URL(
+ '@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm',
+ import.meta.url
+).toString();
+
+const rendererWasmUrl = new URL(
+ '@myriaddreamin/typst-ts-renderer/pkg/typst_ts_renderer_bg.wasm',
+ import.meta.url
+).toString();
+
+let typstInitPromise: Promise