Repository: facebook/docusaurus
File: packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx (TypeScript)

Code before:
      {unlisted && <Unlisted />}

      <BlogPostItem>{children}</BlogPostItem>

      {(nextItem || prevItem) && (
        <BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
      )}
    </BlogLayout>
  );
}

export default function BlogPostPage(props: Props): JSX.Element {
  const BlogPostContent = props.content;
  return (
    <BlogPostProvider content={props.content} isBlogPostPage>
      <HtmlClassNameProvider
        className={clsx(
          ThemeClassNames.wrapper.blogPages,
          ThemeClassNames.page.blogPostPage,
        )}>
        <BlogPostPageMetadata />
        <BlogPostPageStructuredData
          assets={props.content.assets}
          metadata={props.content.metadata}
          blogMetadata={props.blogMetadata}
        />
        <BlogPostPageContent sidebar={props.sidebar}>
          <BlogPostContent />
        </BlogPostPageContent>
      </HtmlClassNameProvider>
    </BlogPostProvider>
  );
}


Review comment (security):
I'd prefer this component to be "prop-less" instead, similar to the `<BlogPostPageMetadata />` component above

The reason is, prop-less components that users swizzle are less sensitive to internal theme refactors such as adding/removing/renaming props, and are less likely to break on Docusaurus version upgrades.

The structured data component could receive that data by other means:

```tsx
const {assets, metadata} = useBlogPost();
```


For the blog metadata, you could use an undocumented "route context" feature that we haven't used much yet but I'd like to move internal usage to it progressively:

```tsx
addRoute({
            path: metadata.permalink,
            component: blogPostComponent,
            exact: true,
            modules: {
              sidebar: aliasedSource(sidebarProp),
              content: metadata.source,
            },
            context: {
              blogMetadata: aliasedSource(blogMetadataPath),
            },
});
```

```tsx
import useRouteContext from '@docusaurus/useRouteContext';

function useBlogMetadata() {
  return useRouteContext().data.blogMetadata as BlogMetadata;
}
```

Eventually we could put this hook in a blog client export, similarly to what we do for the docs plugin (see code importing `@docusaurus/plugin-content-docs/client`, aka `docusaurus/packages/docusaurus-plugin-content-docs/src/client/index.ts` 

I'd prefer this approach because in the future I'd like to have a parent route for your whole blog that declares this context, and makes it accessible to all the blog pages (not just the blog post page but also tags etc)

Let me know if you need help doing this refactoring, that's a bit out of the scope of supporting structured data 😄 

Code after:
      {unlisted && <Unlisted />}

      <BlogPostItem>{children}</BlogPostItem>

      {(nextItem || prevItem) && (
        <BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
      )}
    </BlogLayout>
  );
}

export default function BlogPostPage(props: Props): JSX.Element {
  const BlogPostContent = props.content;
  return (
    <BlogPostProvider content={props.content} isBlogPostPage>
      <HtmlClassNameProvider
        className={clsx(
          ThemeClassNames.wrapper.blogPages,
          ThemeClassNames.page.blogPostPage,
        )}>
        <BlogPostPageMetadata />
        <BlogPostPageStructuredData />
        <BlogPostPageContent sidebar={props.sidebar}>
          <BlogPostContent />
        </BlogPostPageContent>
      </HtmlClassNameProvider>
    </BlogPostProvider>
  );
}