Skip to content

Instantly share code, notes, and snippets.

@andersr
Created December 23, 2024 15:58
Show Gist options
  • Save andersr/87079229e60469429d200ab0177f08d0 to your computer and use it in GitHub Desktop.
Save andersr/87079229e60469429d200ab0177f08d0 to your computer and use it in GitHub Desktop.
Main component in my previous NextJS / React app for rendering mdx blog posts
import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote";
import { CodeBlock, Link, Page } from "~/components";
import { PostData, PostSummaryData } from "~/models";
import Gist from "react-gist";
import Emoji from "a11y-react-emoji";
import { POSTS_DIR, TEST_IDS } from "~/shared";
import React from "react";
import { PullQuote } from "../PullQuote/PullQuote";
import { CalloutBox } from "../CalloutBox/CalloutBox";
import { Image } from "../Image";
import { PostMetaInfo } from "../PostMetaInfo";
import ghSlugger from "github-slugger";
import { DiscussionEmbed } from "disqus-react";
import { GiphyEmbed } from "../GiphyEmbed";
import { BetterCodeBlock } from "../BetterCodeBlock";
function h2Element(props: any) {
// toc-target is used to enable offset below the app header for TOC anchor targets
return (
<h2 className="toc-target" {...props}>
{props.children}
</h2>
);
}
const components = {
pre: (props: any) => <BetterCodeBlock {...props} />,
h2: h2Element,
CodeBlock,
Gist,
Emoji,
PullQuote,
Image,
CalloutBox,
GiphyEmbed,
};
interface Props {
post: PostData;
mdxContent: MDXRemoteSerializeResult;
previous: PostSummaryData | null;
next: PostSummaryData | null;
headings: string[];
}
// TODO: refactor metadata info - lots of duplication between mobile and desktop
export function ViewPost({
post,
mdxContent,
previous,
next,
headings,
}: Props) {
const origin =
typeof window !== "undefined" && window.location.origin
? window.location.origin
: "";
if (!post) {
return <Page title={"Sorry, there was a problem displaying this post"} />;
}
const imageCredit = post.imageCredit ? (
<a
href={post.imageCredit[0]}
className=" text-xs text-slate-400 no-underline"
>
Photo by {post.imageCredit[1]}
</a>
) : null;
const featuredImage = post.featuredImage
? `/images/${post.featuredImage[0]}`
: undefined;
return (
<Page
title={`${post.status !== "published" ? "[D] " : ""}${post.title}`}
metaDescription={post.description}
ogImageUrl={`${origin}${featuredImage}`}
>
{featuredImage && (
<figure className="my-0">
<Image
src={featuredImage}
alt={post.featuredImage ? post.featuredImage[1] : undefined}
priority
/>
{imageCredit && (
<figcaption className="text-right m-0">{imageCredit}</figcaption>
)}
</figure>
)}
<PostMetaInfo post={post} />
{post.toc && headings?.length > 0 && (
<div className="toc">
<h2>Contents</h2>
<ul>
{headings.map((h) => {
const slug = ghSlugger.slug(h.trim());
return (
<li key={slug}>
<a href={`#${slug}`}>{h}</a>
</li>
);
})}
</ul>
<hr />
</div>
)}
<MDXRemote {...mdxContent} components={components} />
<div className="text-center text-slate-400 mb-6 px-4">
<hr />
{previous && (
<div className={`${next ? "pb-2" : ""}`}>
<Link
href={`/${POSTS_DIR}/${previous.slug}`}
testId={TEST_IDS.previousPostLink}
>
Previous post: {previous.title}
</Link>
</div>
)}
{next && (
<div>
<Link
href={`/${POSTS_DIR}/${next.slug}`}
testId={TEST_IDS.nextPostLink}
>
Next post: {next.title}
</Link>
</div>
)}
</div>
<hr />
<div className="px-5 md:px-0">
<DiscussionEmbed
shortname="https-andersco-web-vercel-app"
config={{
url: `https://www.anders.co/${POSTS_DIR}/${post.slug}`,
identifier: post.slug,
title: post.title,
language: "en",
}}
/>
</div>
</Page>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment