11import { renderMarkdownReact } from '@tanstack/markdown/react'
22import * as React from 'react'
33import { InlineCode , MarkdownImg } from '~/ui'
4- import { parseSiteMarkdown , type MarkdownDocument } from '~/utils/markdown'
4+ import {
5+ findFirstImageSrc ,
6+ parseSiteMarkdown ,
7+ type MarkdownDocument ,
8+ } from '~/utils/markdown'
59import { isSafeHttpUrl } from '~/utils/url-boundary'
610import { CodeBlock } from './CodeBlock'
711import { MarkdownLink } from './MarkdownLink'
@@ -19,27 +23,35 @@ export type MarkdownProps = {
1923 content ?: string
2024 document ?: MarkdownDocument
2125 preserveTabPanels ?: boolean
26+ /** Render the first image in the document as high-priority/eager (e.g. blog post hero images) */
27+ eagerFirstImage ?: boolean
2228}
2329
2430export function Markdown ( {
2531 content,
2632 document,
2733 preserveTabPanels,
34+ eagerFirstImage,
2835} : MarkdownProps ) {
2936 const parsed = React . useMemo (
3037 ( ) => document ?? parseSiteMarkdown ( content ?? '' ) ,
3138 [ content , document ] ,
3239 )
3340
3441 return React . useMemo ( ( ) => {
42+ const firstImageSrc = eagerFirstImage
43+ ? findFirstImageSrc ( parsed )
44+ : undefined
45+
3546 return renderMarkdownReact ( parsed , {
3647 allowHtml : true ,
3748 components : createMarkdownComponents ( {
3849 preserveTabPanels,
50+ firstImageSrc,
3951 } ) ,
4052 headingAnchors,
4153 } )
42- } , [ parsed , preserveTabPanels ] )
54+ } , [ parsed , preserveTabPanels , eagerFirstImage ] )
4355}
4456
4557const headingAnchors = {
@@ -160,7 +172,9 @@ function TableElement({
160172 )
161173}
162174
163- function createMarkdownComponents ( options : MarkdownRenderOptions = { } ) {
175+ function createMarkdownComponents (
176+ options : MarkdownRenderOptions & { firstImageSrc ?: string } = { } ,
177+ ) {
164178 function MdCommentComponentWithOptions (
165179 props : React . ComponentProps < typeof MdCommentComponent > ,
166180 ) {
@@ -172,6 +186,13 @@ function createMarkdownComponents(options: MarkdownRenderOptions = {}) {
172186 )
173187 }
174188
189+ function ImgElement ( props : React . ComponentProps < typeof MarkdownImg > ) {
190+ const isFirstImage =
191+ options . firstImageSrc !== undefined && props . src === options . firstImageSrc
192+
193+ return < MarkdownImg { ...props } priority = { isFirstImage } />
194+ }
195+
175196 return {
176197 a : LinkElement ,
177198 code : CodeElement ,
@@ -184,7 +205,7 @@ function createMarkdownComponents(options: MarkdownRenderOptions = {}) {
184205 h5 : createHeadingComponent ( 'h5' ) ,
185206 h6 : createHeadingComponent ( 'h6' ) ,
186207 iframe : MarkdownIframe ,
187- img : MarkdownImg ,
208+ img : ImgElement ,
188209 'md-comment-component' : MdCommentComponentWithOptions ,
189210 'md-framework-panel' : MdFrameworkPanel ,
190211 'md-tab-panel' : MdTabPanel ,
0 commit comments