Recently I have been building my portfolio site using NextJs/TailwindCSS.
Here is the link to the site - Visit here
It's still a work in progress, here is the source code for the site.
In the site I have used markdown files to manage the Projects data on the site.
Today we will discuss how we can use the markdown files to generate contents for a NextJS site with Server Components and integrate with Typescript.
So let's dive in...
Why next-mdx-remote
...
If we follow the official documentation there it is mentioned that how we can basically use .mdx
to generate our JSX components which we can render on our site.
But following this way can be a bit difficult as it will be very difficult to manage dynamically generated content or if the markdown is coming from somewhere else like some CMS or some other place from the file system.
To solve this issue we can use a community package next-mdx-remote
. This package actually compiles a given markdown string and generates HTML, so it will solve our problem of maintaining markdown if we want to scale in future and it is coming from some other database or something.
Integrating next-mdx-remote
to React Server components in NextJS.
Installation
npm install next-mdx-remote
Basic Usage
As we are using react server components we can simply fetch the markdown data from a file and display it with the MDXRemote
component.
// page.tsx
export default async function IndexPage() {
const markdown_data = await getProject();
if (markdown_data === "") {
return notFound();
}
return (
<>
<MDXRemote source={markdown_data} />
</>
);
}
Advance Usage
We can setup our own custom components when generating the HTML form the Markdown files.
For this we can create our own component which will wrap the libraries MDXRemote
component.
// mdx-remote.ts
import { MDXRemote, MDXRemoteProps } from "next-mdx-remote/rsc";
const components = {
h1: (props: ComponentProps<"h1">) => <HeaderOne {...props}>{props.children}</HeaderOne>,
h2: (props: ComponentProps<"h2">) => <HeaderTwo {...props}>{props.children}</HeaderTwo>,
p: (props: ComponentProps<"p">) => <Paragragraph {...props}>{props.children}</Paragragraph>,
img: (props: ComponentProps<"img">) => <MdxImage {...props} />,
a: (props: ComponentProps<"a">) => <MdxLink {...props}>{props.children}</MdxLink>,
};
export function CustomMDX(props: MDXRemoteProps) {
return (
<MDXRemote
{...props}
components={{ ...components, ...(props.components || {}) }}
/>
);
}
And these HeaderOne, HeaderTwo, etc can be simple components with our custom styles.
For example...
// HeaderOne.tsx
import { ComponentProps } from "react";
export function HeaderOne({ children, ...props }: ComponentProps<"h1">) {
return (
<h1 {...props} className="mb-6 mt-16 text-5xl">
{children}
</h1>
);
}
// MDXImage.tsx
import Image from "next/image";
import { ComponentProps } from "react";
type MdxImageProps = ComponentProps<"img">;
export function MdxImage(props: MdxImageProps) {
return (
<div className="flex justify-center">
<div className="border-4 border-primary">
<Image
{...props}
src={props.src ?? ""}
alt={props.alt ?? ""}
width={500}
height={300}
/>
</div>
</div>
);
}
Now we have created our custom wrapper component for the MDXRemote
we can use this in our server component.
// /projects/[title]/page.tsx
export default async function IndexPage({
params,
}: {
params: { title: string };
}) {
const markdown_data = await getProjectWithTitle(params.title);
if (markdown_data === "") {
return notFound();
}
return (
<>
<MDXRemote source={markdown_data} />
</>
);
}
Now as we are using NextJS we can further more optise our site by staticly generating the page with generateStaticParams
function
// /projects/[title]/page.tsx
...
export async function generateStaticParams() {
return getProjectTitles();
}
The source code for the above can be found here
Conclusion
So, in conclusion we discussed how we can integrate this community package next-mdx-remote
for managing content with markdown and also how we can use Typescript to add proper types and have proper type safe development.
If you have any discussions regarding the topic feel free to comment below.
Top comments (0)