{"id":20466,"date":"2023-04-05T09:30:55","date_gmt":"2023-04-05T07:30:55","guid":{"rendered":"https:\/\/www.codemotion.com\/magazine\/?p=20466"},"modified":"2023-09-07T09:55:25","modified_gmt":"2023-09-07T07:55:25","slug":"how-to-create-an-mdx-blog-in-typescript-with-next-js","status":"publish","type":"post","link":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/","title":{"rendered":"How to Create an MDX Blog in TypeScript With Next.js"},"content":{"rendered":"\n<p>MDX is a powerful combination of Markdown and React components that allows you to create dynamic and interactive content. This makes it the perfect markup language for creating a blog. Next.js natively supports MDX, so let&#8217;s use both and create an MDX Next.js TypeScript blog!<\/p>\n\n\n\n<p>At the end of this tutorial, you will know how to build the following MDX-based blog in Next.js and TypeScript. Here you can <a href=\"https:\/\/codesandbox.io\/p\/sandbox\/mdx-blog-in-next-js-and-typescript-z2uh10?file=README.md\" target=\"_blank\" aria-label=\"check a live demo of how the app would look (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">check a live demo of how the app would look<\/a> like.<\/p>\n\n\n\n<p>Let&#8217;s dive into MDX in Next.js!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-is-mdx\">What Is MDX?<\/h2>\n\n\n\n<p><a href=\"https:\/\/mdxjs.com\/\">MDX<\/a>, short for Markdown eXtended, is a markup language that supports the use of JSX within Markdown documents. In other words, MDX combines the simplicity of Markdown with the power of React components. This enables you to create interactive content with no effort.<\/p>\n\n\n\n<p>This is what a sample of MDX looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">import SomeComponent from '.\/SomeComponent';\n\n# My MDX Blog Post\n\nHere's some text for my blog post. I can include React components like this:\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SomeComponent<\/span> <span class=\"hljs-attr\">prop1<\/span>=<span class=\"hljs-string\">\"value1\"<\/span> <span class=\"hljs-attr\">prop2<\/span>=<span class=\"hljs-string\">\"value2\"<\/span> \/&gt;<\/span>\n\nI can also include regular Markdown:\n\n## Section Heading\n\n- List Item 1\n- List Item 2\n- List Item 3<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Typically, MDX is used for content that requires both rich formatting and interactivity, such as documentation or blog posts. By allowing React components to be embedded directly into a Markdown document, MDX simplifies the creation of dynamic content that would be difficult to achieve with simple Markdown.<\/p>\n\n\n\n<p>MDX is supported by several libraries and frameworks, including Next.js, Gatsby, Nuxt, and other static site generators. It is also endorsed by popular documentation sites like Storybook and Docz.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-markdown-mdx-in-next-js\">Markdown\/MDX in Next.js<\/h2>\n\n\n\n<p>Next.js is a popular framework for building server-side web applications with React. Specifically, it comes with built-in support for Markdown and MDX through several tools, including <code><a aria-label=\"[next-mdx-remote] (opens in a new tab)\" href=\"https:\/\/github.com\/hashicorp\/next-mdx-remote\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"ek-link\">next-mdx-remote<\/a><\/code>. This package developed by the community allows Markdown or MDX content to be fetched directly inside <code>getStaticProps()<\/code> or <code>getStaticPaths()<\/code> with no extra configuration required.<\/p>\n\n\n\n<p>With <code>next-mdx-remote<\/code>, you can load MDX content from a variety of sources, including local files, remote URLs, or a database. The package also comes with a powerful MDX render. This is able to transform MDX content into React components and can be configured with custom UI components.<\/p>\n\n\n\n<p>Take a look at <a href=\"https:\/\/nextjs.org\/blog\/markdown\">the official documentation<\/a> to find out more about using Markdown and MDX. Now, it is time to learn how to build a blog based on MDX files in TypeScript with Next.js!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-building-an-mdx-blog-in-next-js-and-typescript\">Building an MDX Blog in Next.js and TypeScript<\/h2>\n\n\n\n<p>Before getting started, make sure you have npm 18+ installed on your machine. Otherwise, download it <a href=\"https:\/\/nodejs.org\/en\">here<\/a>.<\/p>\n\n\n\n<p>Follow this step-by-step tutorial and learn how to build an MDX-based blog with TS in Next.js!<\/p>\n\n\n\n<p><strong>Set up a Next.js TypeScript project<\/strong><br><a href=\"https:\/\/nextjs.org\/docs\/basic-features\/typescript\">Next.js officially supports TypeScript<\/a>. Launch the command below in the terminal to create a new Next.js TypeScript project with:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">npx<\/span> <span class=\"hljs-selector-tag\">create-next-app<\/span><span class=\"hljs-keyword\">@latest<\/span> --ts<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You will be asked some questions. Answer as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">\u221a What is your project named? ... nextjs-markdown-blog\n\u221a Would you like to use ESLint <span class=\"hljs-keyword\">with<\/span> <span class=\"hljs-keyword\">this<\/span> project? ... Yes\n\u221a Would you like to use <span class=\"hljs-string\">`src\/`<\/span> directory <span class=\"hljs-keyword\">with<\/span> <span class=\"hljs-keyword\">this<\/span> project? ... No\n\u221a Would you like to use experimental <span class=\"hljs-string\">`app\/`<\/span> directory <span class=\"hljs-keyword\">with<\/span> <span class=\"hljs-keyword\">this<\/span> project? ... No\n\u221a What <span class=\"hljs-keyword\">import<\/span> alias would you like configured? ... @<span class=\"hljs-comment\">\/*<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This will initialize a Next.js TS project inside the <code>nextjs-markdown-blog<\/code> directory. Enter the folder in the terminal and launch the Next.js demo app with:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">cd nextjs-markdown-blog\nnpm run dev <\/code><\/span><\/pre>\n\n\n<p>If everything went as expected, you should be seeing the default <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/create-next-app\">Create Next App<\/a> view below:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/paper-attachments.dropboxusercontent.com\/s_191A662EDA462332388B4BD2B7D72CAA60238F6E34C4854D37969598EE3708A1_1679600583465_image.png\" alt=\"The Create Next App general view\"\/><\/figure>\n\n\n\n<p>Great! You now have a Next.js project ready to be turned into an MDX blog. Keep reading and learn how!<\/p>\n\n\n\n<p><strong>Populate your blog with some MDX files<\/strong><br>Your blog will consist of articles read from MDX files. Create a <code>_posts<\/code> folder in your project and populate it with some <code>.mdx<\/code> articles. If you lack imagination or time, you can use a <a href=\"https:\/\/www.lipsum.com\/\">Lorem Ipsum<\/a> generator or ask ChatGPT to generate some content for you.<\/p>\n\n\n\n<p>This is what a sample <code>top-programming-languages.mdx<\/code> blog post file may look like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">---\ntitle: Top <span class=\"hljs-number\">5<\/span> Programming Languages to Learn\ndescription: A brief overview of the most in-demand programming languages you should consider learning in <span class=\"hljs-number\">2023<\/span> to stay ahead of the curve in the tech industry.\npreviewImage: https:<span class=\"hljs-comment\">\/\/source.unsplash.com\/A-NVHPka9Rk\/1920x1280<\/span>\n---\n\n<span class=\"hljs-comment\"># Top 5 Programming Languages to Learn<\/span>\n&lt;HeroImage src=<span class=\"hljs-string\">\"https:\/\/source.unsplash.com\/A-NVHPka9Rk\/1920x1280\"<\/span> alt={<span class=\"hljs-string\">\"main image\"<\/span>} \/&gt;\n\nProgramming languages are the backbone of the digital world, powering everything from websites <span class=\"hljs-keyword\">and<\/span> mobile apps to data analysis <span class=\"hljs-keyword\">and<\/span> artificial intelligence. With so many programming languages to choose from, it can be tough to know where to start. In this article, we<span class=\"hljs-string\">'ll discuss the top 5 programming languages to learn in 2023.\n{\/* omitted for brevity... *\/}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Take a look at the special syntax used at the beginning of the file to define a <code>title<\/code>, <code>description<\/code>, and <code>previewImage<\/code>. That is a YAML frontmatter. If you are not familiar with this concept, a frontmatter is a section of metadata enclosed in triple-dashed lines <code>---<\/code> that appears at the beginning of a Markdown or MDX document. Typically, it is in <a href=\"https:\/\/en.wikipedia.org\/wiki\/YAML\">YAML<\/a> format and provides useful information to describe the content stored in the Markdown\/MDX document.<\/p>\n\n\n\n<p>Also, note the <code>HeroImage<\/code> component used inside the <code>.mdx<\/code> file. That is a custom React component that will be rendered together with the content contained in the MDX file. You will learn how this is possible in the next steps.<\/p>\n\n\n\n<p><strong>Define the MDX components<\/strong><br>Each JSX element mentioned in <code>.mdx<\/code> files must have a respective React component in the Next.js project. Add a <code>components\/mdx<\/code> folder and prepare to write some custom MDX components. For example, this is the <code>HeroImage<\/code> React file mentioned early:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ components\/mdx\/HeroImage.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> Image <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/image\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">HeroImage<\/span>(<span class=\"hljs-params\">{ src, alt }: { src: string; alt: string }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">mdx-hero-image<\/span>\"}&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{src}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{alt}<\/span> <span class=\"hljs-attr\">fill<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>As you can note, it wraps the <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/next\/image\" class=\"ek-link\">Next.js\u2019s<\/a> <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/next\/image\" class=\"ek-link\">component<\/a> in a custom <code>div<\/code> that you can style as you wish. In particular, <code>HeroImage<\/code> takes care of representing the main image associated with each article.<\/p>\n\n\n\n<p>Keep in mind that you can also define React components to override the default HTML element used by <code>next-mdx-remote<\/code> to render MDX content. For example, define a special <code>H1<\/code> component for Markdown titles as below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ components\/mdx\/H1.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">H1<\/span>(<span class=\"hljs-params\">{ children }: { children?: React.ReactNode }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mdx-h1\"<\/span>&gt;<\/span>{children}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Similarly, you can create an <code>H2<\/code> component for subtitles:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ components\/mdx\/H2.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">P<\/span>(<span class=\"hljs-params\">{ children }: { children?: React.ReactNode }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mdx-h2\"<\/span>&gt;<\/span>{children}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span><\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And a custom <code>P<\/code> component for text paragraphs:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ components\/mdx\/P.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">P<\/span>(<span class=\"hljs-params\">{ children }: { children?: React.ReactNode }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mdx-p\"<\/span>&gt;<\/span>{children}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>What these components have in common is that they are characterized by a custom CSS class. This gives you the ability to style these MDX components as you like, for example with the following CSS rules in <code>styles\/global.css<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">.mdx-h1 {\n  font-size: <span class=\"hljs-number\">52<\/span>px;\n  color: <span class=\"hljs-comment\">#0d1d30;<\/span>\n}\n\n.mdx-h2 {\n  font-size: <span class=\"hljs-number\">36<\/span>px;\n  color: <span class=\"hljs-comment\">#0d1d30;<\/span>\n}\n\n.mdx-p {\n  font-size: <span class=\"hljs-number\">20<\/span>px;\n  margin-bottom: <span class=\"hljs-number\">1.5<\/span>em;\n  line-height: <span class=\"hljs-number\">1.6<\/span>em;\n}\n\n.mdx-hero-image {\n  position: relative;\n  width: <span class=\"hljs-number\">100<\/span>%;\n  height: <span class=\"hljs-number\">600<\/span>px;\n\n  img {\n    border-radius: <span class=\"hljs-number\">10<\/span>px;\n    object-fit: cover;\n    object-position: center;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Time to learn how to use these custom MDX components with <code>next-mdx-remote<\/code>.<\/p>\n\n\n\n<p>Render MDX in Next.js**<br>To add server-side MDX rendering capabilities to Next.js, you need to add <code><a href=\"http:\/\/(https:\/\/www.npmjs.com\/package\/next-mdx-remote)\" class=\"ek-link\">next-mdx-remote<\/a><\/code> to your project&#8217;s dependencies with:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">npm install next-mdx-remote<\/code><\/span><\/pre>\n\n\n<p>You are now ready to create the Next.js dynamic-content page for your blog posts. In detail, you will use Next.js\u2019s <a href=\"https:\/\/blog.logrocket.com\/implementing-ssr-next-js-dynamic-routing-prefetching\/\">dynamic routes<\/a> feature. This allows you to populate a template page with the MDX content stored in each<code>.mdx<\/code> file within the <code>_posts<\/code> directory.<\/p>\n\n\n\n<p>To achieve that, define a <code>[slug].ts<\/code> file inside <code>pages<\/code> as below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ pages\/&#91;slug].tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> fs <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"fs\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { GetStaticPropsContext, InferGetStaticPropsType } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { serialize } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next-mdx-remote\/serialize\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { MDXRemote } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next-mdx-remote\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> Head <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/head\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> H1 <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/mdx\/H1\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> HeroImage <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/mdx\/HeroImage\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> P <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/mdx\/P\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> H2 <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/mdx\/H2\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PostPage<\/span>(<span class=\"hljs-params\">{ source }: InferGetStaticPropsType&lt;typeof getStaticProps&gt;<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>{source.frontmatter.title as string}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">MDXRemote<\/span>\n        {<span class=\"hljs-attr\">...source<\/span>}\n        \/\/ <span class=\"hljs-attr\">specifying<\/span> <span class=\"hljs-attr\">the<\/span> <span class=\"hljs-attr\">custom<\/span> <span class=\"hljs-attr\">MDX<\/span> <span class=\"hljs-attr\">components<\/span>\n        <span class=\"hljs-attr\">components<\/span>=<span class=\"hljs-string\">{{<\/span>\n          <span class=\"hljs-attr\">h1:<\/span> <span class=\"hljs-attr\">H1<\/span>,\n          <span class=\"hljs-attr\">h2:<\/span> <span class=\"hljs-attr\">H2<\/span>,\n          <span class=\"hljs-attr\">p:<\/span> <span class=\"hljs-attr\">P<\/span>,\n          <span class=\"hljs-attr\">HeroImage<\/span>,\n        }}\n      \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getStaticPaths<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">paths<\/span>: &#91;], <span class=\"hljs-attr\">fallback<\/span>: <span class=\"hljs-string\">\"blocking\"<\/span> }\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getStaticProps<\/span>(<span class=\"hljs-params\">\n  ctx: GetStaticPropsContext&lt;{\n    slug: string\n  }&gt;,\n<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> { slug } = ctx.params!\n\n  <span class=\"hljs-comment\">\/\/ retrieve the MDX blog post file associated<\/span>\n  <span class=\"hljs-comment\">\/\/ with the specified slug parameter<\/span>\n  <span class=\"hljs-keyword\">const<\/span> postFile = fs.readFileSync(<span class=\"hljs-string\">`_posts\/<span class=\"hljs-subst\">${slug}<\/span>.mdx`<\/span>)\n\n  <span class=\"hljs-comment\">\/\/ read the MDX serialized content along with the frontmatter<\/span>\n  <span class=\"hljs-comment\">\/\/ from the .mdx blog post file<\/span>\n  <span class=\"hljs-keyword\">const<\/span> mdxSource = <span class=\"hljs-keyword\">await<\/span> serialize(postFile, { <span class=\"hljs-attr\">parseFrontmatter<\/span>: <span class=\"hljs-literal\">true<\/span> })\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">props<\/span>: {\n      <span class=\"hljs-attr\">source<\/span>: mdxSource,\n    },\n    <span class=\"hljs-comment\">\/\/ enable ISR<\/span>\n    <span class=\"hljs-attr\">revalidate<\/span>: <span class=\"hljs-number\">60<\/span>,\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>slug<\/code> parameter read from the page URL by Next.js is passed to <code><a href=\"http:\/\/(https:\/\/nextjs.org\/docs\/basic-features\/data-fetching\/get-static-props)\" class=\"ek-link\">getStaticProps()<\/a><\/code>, where it is used to load the corresponding <code>_posts\\[slug].mdx<\/code> file. Then, its MDX content gets converted into JSX by the <code>serialize()<\/code> function exposed by <code>next-mdx-remote<\/code>. Note the <code>parseFrontmatter<\/code> config flag set to <code>true<\/code> to parse also the frontmatter contained in the <code>.mdx<\/code> file.<\/p>\n\n\n\n<p>Finally, the resulting object is passed to the server-side <code>PostPage<\/code> component. Here, the <code><a aria-label=\"[MDXRemote] (opens in a new tab)\" href=\"https:\/\/github.com\/hashicorp\/next-mdx-remote#apis\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"ek-link\">MDXRemote<\/a><\/code> parser component provided by <code>next-mdx-remote<\/code> receives the serialized source and the list of custom MDX custom components, using them to render the blog post in HTML.<\/p>\n\n\n\n<p>Since blog posts are likely to be updated over time, you should use the <a href=\"https:\/\/nextjs.org\/docs\/basic-features\/data-fetching\/incremental-static-regeneration\">Incremental Static Regeneration (ISR)<\/a> approach. That is enabled through the <code>revalidate<\/code> option and allows static pages to be incrementally updated without requiring a complete rebuild of the Next.js app.<\/p>\n\n\n\n<p>Now, suppose you have a <code>top-programming-languages.mdx<\/code> file inside <code>_posts<\/code>. Launch your Next.js app and visit the <code>http:\/\/localhost:3000\/blog\/top-programming-languages<\/code> page in the browser. In this case, <code>slug<\/code> will contain the <code>\"top-programming-languages\"<\/code> string and Next.js will load the desired <code>.mdx<\/code> file.<\/p>\n\n\n\n<p>This is what Next.js will produce:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/paper-attachments.dropboxusercontent.com\/s_191A662EDA462332388B4BD2B7D72CAA60238F6E34C4854D37969598EE3708A1_1679605062586_image.png\" alt=\"The image domain Next.js error\"\/><\/figure>\n\n\n\n<p>This error occurs because <code>HeroImage<\/code> tries to display an image coming from <a href=\"https:\/\/unsplash.com\/\">Unsplash<\/a>, one the most popular image provider. By default, <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/next\/image#domains\" class=\"ek-link\">Next.js\u2019s<\/a> <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/next\/image#domains\" class=\"ek-link\">API blocks external domains<\/a> for security reasons. Configure Next.js to work with Unsplash by updating the <code>next.config.js<\/code> file with the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ next.config.jsx<\/span>\n\n<span class=\"hljs-comment\">\/** <span class=\"hljs-doctag\">@type <span class=\"hljs-type\">{import('next').NextConfig}<\/span> <\/span>*\/<\/span>\n<span class=\"hljs-keyword\">const<\/span> nextConfig = {\n  <span class=\"hljs-attr\">reactStrictMode<\/span>: <span class=\"hljs-literal\">true<\/span>,\n  <span class=\"hljs-attr\">images<\/span>: {\n    <span class=\"hljs-attr\">domains<\/span>: &#91;<span class=\"hljs-string\">\"source.unsplash.com\"<\/span>],\n  },\n}\n\n<span class=\"hljs-built_in\">module<\/span>.exports = nextConfig<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Restart the development server and visit <code>http:\/\/localhost:3000\/blog\/top-programming-languages<\/code> again. This time, you should see your MDX-based blog post:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/paper-attachments.dropboxusercontent.com\/s_191A662EDA462332388B4BD2B7D72CAA60238F6E34C4854D37969598EE3708A1_1679605118633_image.png\" alt=\"An MDX blog post rendered by next-mdx-remote\"\/><\/figure>\n\n\n\n<p>Fantastic! It does not (yet) look good, but it works!<\/p>\n\n\n\n<p><strong>Add a homepage<\/strong><br>Your blog needs a fancy homepage containing the latest blog posts. Next.js stores the homepage of your site in <code>pages\/index.ts<\/code>x. Replace that file with the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ pages\/index.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> PostCard <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/PostCard\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { InferGetStaticPropsType } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> fs <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"fs\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { serialize } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next-mdx-remote\/serialize\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> path <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"path\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { PostPreview } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/types\/posts\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\">{ postPreviews }: InferGetStaticPropsType&lt;typeof getStaticProps&gt;<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      {postPreviews.map((postPreview, i) =&gt; {\n        return (\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{i}<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PostCard<\/span> <span class=\"hljs-attr\">postPreview<\/span>=<span class=\"hljs-string\">{postPreview}<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        )\n      })}\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getStaticProps<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ get all MDX files<\/span>\n  <span class=\"hljs-keyword\">const<\/span> postFilePaths = fs.readdirSync(<span class=\"hljs-string\">\"_posts\"<\/span>).filter(<span class=\"hljs-function\">(<span class=\"hljs-params\">postFilePath<\/span>) =&gt;<\/span> {\n    <span class=\"hljs-keyword\">return<\/span> path.extname(postFilePath).toLowerCase() === <span class=\"hljs-string\">\".mdx\"<\/span>\n  })\n\n  <span class=\"hljs-keyword\">const<\/span> postPreviews: PostPreview&#91;] = &#91;]\n\n  <span class=\"hljs-comment\">\/\/ read the frontmatter for each file<\/span>\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> postFilePath <span class=\"hljs-keyword\">of<\/span> postFilePaths) {\n    <span class=\"hljs-keyword\">const<\/span> postFile = fs.readFileSync(<span class=\"hljs-string\">`_posts\/<span class=\"hljs-subst\">${postFilePath}<\/span>`<\/span>, <span class=\"hljs-string\">\"utf8\"<\/span>)\n\n    <span class=\"hljs-comment\">\/\/ serialize the MDX content to a React-compatible format<\/span>\n    <span class=\"hljs-comment\">\/\/ and parse the frontmatter<\/span>\n    <span class=\"hljs-keyword\">const<\/span> serializedPost = <span class=\"hljs-keyword\">await<\/span> serialize(postFile, {\n      <span class=\"hljs-attr\">parseFrontmatter<\/span>: <span class=\"hljs-literal\">true<\/span>,\n    })\n\n    postPreviews.push({\n      ...serializedPost.frontmatter,\n      <span class=\"hljs-comment\">\/\/ add the slug to the frontmatter info<\/span>\n      <span class=\"hljs-attr\">slug<\/span>: postFilePath.replace(<span class=\"hljs-string\">\".mdx\"<\/span>, <span class=\"hljs-string\">\"\"<\/span>),\n    } <span class=\"hljs-keyword\">as<\/span> PostPreview)\n  }\n\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">props<\/span>: {\n      <span class=\"hljs-attr\">postPreviews<\/span>: postPreviews,\n    },\n    <span class=\"hljs-comment\">\/\/ enable ISR<\/span>\n    <span class=\"hljs-attr\">revalidate<\/span>: <span class=\"hljs-number\">60<\/span>,\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>getStaticProps()<\/code> takes care of loading all <code>.mdx<\/code> files, transforming them into <code>PostPreview<\/code> objects, and rendering them in <code>PostCard<\/code> components.<\/p>\n\n\n\n<p>This is what the <code>PostPreview<\/code> TypeScript type looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ types\/posts.tsx<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> type PostPreview = {\n  <span class=\"hljs-attr\">title<\/span>: string\n  <span class=\"hljs-attr\">description<\/span>: string\n  <span class=\"hljs-attr\">previewImage<\/span>: string\n  <span class=\"hljs-attr\">slug<\/span>: string\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And this is how the <code>PostCard<\/code> is defined:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { PostPreview } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/types\/posts\"<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PostCard<\/span>(<span class=\"hljs-params\">{ postPreview }: { postPreview: PostPreview }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">post-card<\/span>\"} <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">backgroundImage:<\/span> `<span class=\"hljs-attr\">url<\/span>(${<span class=\"hljs-attr\">postPreview.previewImage<\/span>})` }}&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{postPreview.slug}<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">post-card-content<\/span>\"}&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">post-card-title<\/span>\"}&gt;<\/span>{postPreview.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">post-card-description<\/span>\"}&gt;<\/span>{postPreview.description}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If you style <code>PostCard<\/code> with some CSS rules, <code>http:\/\/localhost:3000<\/code> should appear as in the image below:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/paper-attachments.dropboxusercontent.com\/s_191A662EDA462332388B4BD2B7D72CAA60238F6E34C4854D37969598EE3708A1_1679606001822_image.png\" alt=\"The MDX blog\u2019s homepage\"\/><\/figure>\n\n\n\n<p>Well done! It only remains to add a layout to your blog and style it accordingly!<\/p>\n\n\n\n<p><strong>Style your blog<\/strong><br>The main problem with your blog right now is that it takes up the entire viewport width. You can avoid that with a <a href=\"https:\/\/getbootstrap.com\/docs\/5.0\/layout\/containers\/\">Bootstrap container<\/a>. Install <code><a aria-label=\"[bootstrap] (opens in a new tab)\" href=\"http:\/\/(https:\/\/www.npmjs.com\/package\/bootstrap)\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"ek-link\">bootstrap<\/a><\/code> with:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">npm install bootstrap<\/code><\/span><\/pre>\n\n\n<p>Then, add the following line to <code>_app.tsx<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">\"bootstrap\/dist\/css\/bootstrap.css\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, define a <a href=\"https:\/\/nextjs.org\/docs\/basic-features\/layouts\">Next.js layout<\/a> based on Bootstrap:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ components\/Layout.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> Header <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/Header\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { Inter } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/font\/google\"<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> inter = Inter({ <span class=\"hljs-attr\">subsets<\/span>: &#91;<span class=\"hljs-string\">\"latin\"<\/span>] })\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Layout<\/span>(<span class=\"hljs-params\">{ children }: { children?: React.ReactNode }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{inter.className}<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Header<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">container<\/span>\"}&gt;<\/span>{children}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Take advantage of <a href=\"https:\/\/nextjs.org\/docs\/api-reference\/next\/font\">Next.js font API<\/a> to set a good-looking Google font for your blog. Also, wrap the entire content under the <code>Header<\/code> component with a Bootstrap <code>.container<\/code> div.<\/p>\n\n\n\n<p>If you are wondering, <code>Header<\/code> is nothing more than a simple <code>div<\/code> containing the title of your blog:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ components\/Header.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { Nunito } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/font\/google\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> nunito = Nunito({ <span class=\"hljs-attr\">subsets<\/span>: &#91;<span class=\"hljs-string\">\"latin\"<\/span>] })\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Header<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">header<\/span> <span class=\"hljs-attr\">mb-4<\/span> ${<span class=\"hljs-attr\">nunito.className<\/span>}`}&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">container<\/span>\"}&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>\"<span class=\"hljs-attr\">header-title<\/span> <span class=\"hljs-attr\">mt-4<\/span>\"}&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/\"<\/span>&gt;<\/span>My MDX Blog in Next.js<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, wraps the <code>Component<\/code> instance containing all your Next.js site with <code>Layout<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ _app.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">\"@\/styles\/globals.scss\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> type { AppProps } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/app\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">\"bootstrap\/dist\/css\/bootstrap.css\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> Layout <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/components\/Layout\"<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\">{ Component, pageProps }: AppProps<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Layout<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Component<\/span> {<span class=\"hljs-attr\">...pageProps<\/span>} \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Layout<\/span>&gt;<\/span><\/span>\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>As you can see, the global CSS file <code>styles\/globals.scss<\/code> imported here is a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Sass_(stylesheet_language)\">SASS<\/a> file. SCSS is much more powerful than CSS and makes it easier to style your web application. <a href=\"https:\/\/nextjs.org\/docs\/messages\/install-sass\">Next.js supports SCSS<\/a> after installing <code>sass<\/code> with:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">npm install sass<\/code><\/span><\/pre>\n\n\n<p>Make your Next.js MDX TypeScript blog look original and eye-catching with some SCSS rules!<\/p>\n\n\n\n<p><strong>Put it all together<\/strong><br>You can find the entire code of the MDX-based blog developed in TypeScript with Next.js in the <a href=\"https:\/\/github.com\/Tonel\/nextjs-mdx-typescript-blog\">GitHub repository that supports the article<\/a>. Clone it and launch the blog locally with:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">git <span class=\"hljs-keyword\">clone<\/span> https:<span class=\"hljs-comment\">\/\/github.com\/Tonel\/nextjs-mdx-typescript-blog<\/span>\ncd nextjs-mdx-typescript-blog\nnpm i\nnpm run dev<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Visit <code>htpp:\/\/localhost:3000<\/code> in your browser and <a href=\"https:\/\/www.dropbox.com\/s\/r8hjgvxwnettr2k\/Next.js%20MDX.mp4?dl=0\" target=\"_blank\" aria-label=\"explore the MDX Next.js blog (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">explore the MDX Next.js blog<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\n<p>In this step-by-step guide, you saw how to create an MDX blog with TypeScript and Next.js. With the help of MDX, you can easily mix and match React components with Markdown content, making it easy to create rich and engaging blog posts.<\/p>\n\n\n\n<p>One of the best things about using MDX with Next.js is that you have several options for managing your blog&#8217;s content. You can use a headless CMS to manage your MDX content, or you can update <code>.mdx<\/code> files directly on GitHub. Regardless of which approach you choose, MDX will allow you to create the blog post that fits your needs.<\/p>\n\n\n\n<p>Thanks for reading! We hope you found this article helpful!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong><em>More about TypeScript here:<br><\/em><\/strong><a aria-label=\"Why you should use Typescript for your next project\n (opens in a new tab)\" href=\"https:\/\/www.codemotion.com\/magazine\/backend\/why-you-should-use-typescript-for-your-next-project\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"ek-link\"><em>Why you should use Typescript for your next project<br><\/em><\/a><a aria-label=\"Typescript 10 years after release (opens in a new tab)\" href=\"https:\/\/www.codemotion.com\/magazine\/frontend\/typescript-10-years-after-release\/\" target=\"_blank\" rel=\"noreferrer noopener\" class=\"ek-link\"><em>Typescript 10 years after release<\/em><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>MDX is a powerful combination of Markdown and React components that allows you to create dynamic and interactive content. This makes it the perfect markup language for creating a blog. Next.js natively supports MDX, so let&#8217;s use both and create an MDX Next.js TypeScript blog! At the end of this tutorial, you will know how&#8230; <a class=\"more-link\" href=\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\">Read more<\/a><\/p>\n","protected":false},"author":160,"featured_media":20587,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_editorskit_title_hidden":false,"_editorskit_reading_time":9,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","_uag_custom_page_level_css":"","_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","footnotes":""},"categories":[6],"tags":[10006,9971],"collections":[],"class_list":{"0":"post-20466","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-frontend","8":"tag-nextjs","9":"tag-typescript","10":"entry"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.9 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Creating an MDX Blog With TypeScript and Next.js<\/title>\n<meta name=\"description\" content=\"Follow this step-by-step tutorial and build a file-based blog with mdx-remote in Next.js. Code examples included!\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Create an MDX Blog in TypeScript With Next.js\" \/>\n<meta property=\"og:description\" content=\"Follow this step-by-step tutorial and build a file-based blog with mdx-remote in Next.js. Code examples included!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\" \/>\n<meta property=\"og:site_name\" content=\"Codemotion Magazine\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Codemotion.Italy\/\" \/>\n<meta property=\"article:published_time\" content=\"2023-04-05T07:30:55+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-07T07:55:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"1024\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Antonello Zanini\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@CodemotionIT\" \/>\n<meta name=\"twitter:site\" content=\"@CodemotionIT\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Antonello Zanini\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\"},\"author\":{\"name\":\"Antonello Zanini\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/1fd4f55ec0d9743347559c715b9edf4a\"},\"headline\":\"How to Create an MDX Blog in TypeScript With Next.js\",\"datePublished\":\"2023-04-05T07:30:55+00:00\",\"dateModified\":\"2023-09-07T07:55:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\"},\"wordCount\":1592,\"publisher\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg\",\"keywords\":[\"NextJS\",\"typescript\"],\"articleSection\":[\"Frontend\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\",\"name\":\"Creating an MDX Blog With TypeScript and Next.js\",\"isPartOf\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg\",\"datePublished\":\"2023-04-05T07:30:55+00:00\",\"dateModified\":\"2023-09-07T07:55:25+00:00\",\"description\":\"Follow this step-by-step tutorial and build a file-based blog with mdx-remote in Next.js. Code examples included!\",\"breadcrumb\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg\",\"contentUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg\",\"width\":1024,\"height\":1024,\"caption\":\"Vector illustration of online social communication with male character lying on floor with laptop and corresponding in network chat in flat style isolated on white background.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.codemotion.com\/magazine\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Frontend\",\"item\":\"https:\/\/www.codemotion.com\/magazine\/frontend\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"How to Create an MDX Blog in TypeScript With Next.js\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#website\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/\",\"name\":\"Codemotion Magazine\",\"description\":\"We code the future. Together\",\"publisher\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.codemotion.com\/magazine\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#organization\",\"name\":\"Codemotion\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png\",\"contentUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png\",\"width\":225,\"height\":225,\"caption\":\"Codemotion\"},\"image\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Codemotion.Italy\/\",\"https:\/\/x.com\/CodemotionIT\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/1fd4f55ec0d9743347559c715b9edf4a\",\"name\":\"Antonello Zanini\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/8df9ffe2e0d01ee1cf62c1307c69fd078041934eefd24c47eda05b4f57b4550e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/8df9ffe2e0d01ee1cf62c1307c69fd078041934eefd24c47eda05b4f57b4550e?s=96&d=mm&r=g\",\"caption\":\"Antonello Zanini\"},\"description\":\"I'm a software engineer, but I prefer to call myself a Technology Bishop. Spreading knowledge through writing is my mission.\",\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/antonello-zanini?originalSubdomain=it\"],\"url\":\"https:\/\/www.codemotion.com\/magazine\/author\/antonello-zanini\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Creating an MDX Blog With TypeScript and Next.js","description":"Follow this step-by-step tutorial and build a file-based blog with mdx-remote in Next.js. Code examples included!","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/","og_locale":"en_US","og_type":"article","og_title":"How to Create an MDX Blog in TypeScript With Next.js","og_description":"Follow this step-by-step tutorial and build a file-based blog with mdx-remote in Next.js. Code examples included!","og_url":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/","og_site_name":"Codemotion Magazine","article_publisher":"https:\/\/www.facebook.com\/Codemotion.Italy\/","article_published_time":"2023-04-05T07:30:55+00:00","article_modified_time":"2023-09-07T07:55:25+00:00","og_image":[{"width":1024,"height":1024,"url":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg","type":"image\/jpeg"}],"author":"Antonello Zanini","twitter_card":"summary_large_image","twitter_creator":"@CodemotionIT","twitter_site":"@CodemotionIT","twitter_misc":{"Written by":"Antonello Zanini","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#article","isPartOf":{"@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/"},"author":{"name":"Antonello Zanini","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/1fd4f55ec0d9743347559c715b9edf4a"},"headline":"How to Create an MDX Blog in TypeScript With Next.js","datePublished":"2023-04-05T07:30:55+00:00","dateModified":"2023-09-07T07:55:25+00:00","mainEntityOfPage":{"@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/"},"wordCount":1592,"publisher":{"@id":"https:\/\/www.codemotion.com\/magazine\/#organization"},"image":{"@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage"},"thumbnailUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg","keywords":["NextJS","typescript"],"articleSection":["Frontend"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/","url":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/","name":"Creating an MDX Blog With TypeScript and Next.js","isPartOf":{"@id":"https:\/\/www.codemotion.com\/magazine\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage"},"image":{"@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage"},"thumbnailUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg","datePublished":"2023-04-05T07:30:55+00:00","dateModified":"2023-09-07T07:55:25+00:00","description":"Follow this step-by-step tutorial and build a file-based blog with mdx-remote in Next.js. Code examples included!","breadcrumb":{"@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#primaryimage","url":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg","contentUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg","width":1024,"height":1024,"caption":"Vector illustration of online social communication with male character lying on floor with laptop and corresponding in network chat in flat style isolated on white background."},{"@type":"BreadcrumbList","@id":"https:\/\/www.codemotion.com\/magazine\/frontend\/how-to-create-an-mdx-blog-in-typescript-with-next-js\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.codemotion.com\/magazine\/"},{"@type":"ListItem","position":2,"name":"Frontend","item":"https:\/\/www.codemotion.com\/magazine\/frontend\/"},{"@type":"ListItem","position":3,"name":"How to Create an MDX Blog in TypeScript With Next.js"}]},{"@type":"WebSite","@id":"https:\/\/www.codemotion.com\/magazine\/#website","url":"https:\/\/www.codemotion.com\/magazine\/","name":"Codemotion Magazine","description":"We code the future. Together","publisher":{"@id":"https:\/\/www.codemotion.com\/magazine\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.codemotion.com\/magazine\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.codemotion.com\/magazine\/#organization","name":"Codemotion","url":"https:\/\/www.codemotion.com\/magazine\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/","url":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png","contentUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png","width":225,"height":225,"caption":"Codemotion"},"image":{"@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Codemotion.Italy\/","https:\/\/x.com\/CodemotionIT"]},{"@type":"Person","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/1fd4f55ec0d9743347559c715b9edf4a","name":"Antonello Zanini","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/8df9ffe2e0d01ee1cf62c1307c69fd078041934eefd24c47eda05b4f57b4550e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8df9ffe2e0d01ee1cf62c1307c69fd078041934eefd24c47eda05b4f57b4550e?s=96&d=mm&r=g","caption":"Antonello Zanini"},"description":"I'm a software engineer, but I prefer to call myself a Technology Bishop. Spreading knowledge through writing is my mission.","sameAs":["https:\/\/www.linkedin.com\/in\/antonello-zanini?originalSubdomain=it"],"url":"https:\/\/www.codemotion.com\/magazine\/author\/antonello-zanini\/"}]}},"featured_image_src":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-600x400.jpg","featured_image_src_square":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-600x600.jpg","author_info":{"display_name":"Antonello Zanini","author_link":"https:\/\/www.codemotion.com\/magazine\/author\/antonello-zanini\/"},"uagb_featured_image_src":{"full":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg",1024,1024,false],"thumbnail":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-150x150.jpg",150,150,true],"medium":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-300x300.jpg",300,300,true],"medium_large":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-768x768.jpg",768,768,true],"large":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg",1024,1024,false],"1536x1536":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg",1024,1024,false],"2048x2048":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg",1024,1024,false],"small-home-featured":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936.jpg",100,100,false],"sidebar-featured":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-180x128.jpg",180,128,true],"genesis-singular-images":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-896x504.jpg",896,504,true],"archive-featured":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-400x225.jpg",400,225,true],"gb-block-post-grid-landscape":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-600x400.jpg",600,400,true],"gb-block-post-grid-square":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2023\/03\/iStock-1142124936-600x600.jpg",600,600,true]},"uagb_author_info":{"display_name":"Antonello Zanini","author_link":"https:\/\/www.codemotion.com\/magazine\/author\/antonello-zanini\/"},"uagb_comment_info":0,"uagb_excerpt":"MDX is a powerful combination of Markdown and React components that allows you to create dynamic and interactive content. This makes it the perfect markup language for creating a blog. Next.js natively supports MDX, so let&#8217;s use both and create an MDX Next.js TypeScript blog! At the end of this tutorial, you will know how&#8230;&hellip;","lang":"en","_links":{"self":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts\/20466","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/users\/160"}],"replies":[{"embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/comments?post=20466"}],"version-history":[{"count":15,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts\/20466\/revisions"}],"predecessor-version":[{"id":23111,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts\/20466\/revisions\/23111"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/media\/20587"}],"wp:attachment":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/media?parent=20466"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/categories?post=20466"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/tags?post=20466"},{"taxonomy":"collections","embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/collections?post=20466"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}