Master Your Blog Writing with Shortcodes and MDX Components in Astro & Next.js

By Al Murad Uzzaman| Last Updated: 09 Dec, 2025|06 Mins read
Master Your Blog Writing with Shortcodes and MDX Components in Astro & Next.js

You may have felt limited at times if you've been writing blogs in plain Markdown. I mean, Markdown is great and all, but sometimes you need more than just headings and paragraphs, don't you? That's where shortcodes come in, and believe me, they make a big difference.

Today, I'm going to walk you through how to use shortcodes in your blog posts, and I'll be using examples from two awesome open source projects:

  • Astroplate - A production-ready Astro boilerplate
  • Nextplate - A Next.js boilerplate with the same shortcodes

What is cool? Both projects have the same shortcodes, but they are set up differently for each framework. So, no matter if you're on team Astro or team Next.js, you're good to go!

What Are Shortcodes Anyway?

Shortcodes are like small building pieces that you may add to your blog posts. You don't have to write complicated HTML or JavaScript every time you want a button or an accordion. Instead, you can just use a basic component tag. It's like having a toolbox full of ready-to-use UI components.

Instead of writing a lot of HTML for a beautiful button, you could just write:

<Button label="Click Me" link="/somewhere" style="solid" />

And bang! You have a button that looks great and works well. No trouble, no mess.

Getting Started with MDX

Let's discuss about MDX before we get into the shortcodes. You can use React components right inside your markdown files, which is like Markdown on steroids. That's pretty cool, right?

For Astro Users

You're lucky if you use Astro (or Astroplate). There is built-in support for MDX. In your content directory, just make files with the .mdx extension:

src/
  content/
    blog/
      my-awesome-post.mdx

That's all. Really.

For Next.js Users

If you use Next.js or Nextplate, MDX support is already set up for you. The project uses next-mdx-remote to show MDX material. If you're starting from scratch, you'll need:

npm install next-mdx-remote remark-gfm

The Nextplate boilerplate already has this set up in the MDXContent component, which is in charge of rendering markdown/MDX with shortcode components. You don't need to set up anything else in next.config.js for MDX!

How Nextplate Configures Shortcodes

The MDXContent component in Nextplate makes shortcodes available in all of your blog posts automatically. This is how it works:

Project Structure:

src/
  layouts/
    shortcodes/
      Button.tsx
      Notice.tsx
      Accordion.tsx
      Tabs.tsx
      Tab.tsx
      Youtube.tsx
      Video.tsx
      all.tsx          # Exports all shortcodes
    helpers/
      MDXContent.tsx   # Renders MDX with shortcodes
  content/
    blog/
      *.md             # Your blog posts (use .md extension)

Key Configuration Files:

  1. src/layouts/shortcodes/all.tsx - Exports all shortcode components
  2. src/layouts/helpers/MDXContent.tsx - Uses next-mdx-remote with remark-gfm plugin

You don't need to import anything to use the MDXContent component because it automatically adds all shortcodes to your markdown files.

The Shortcodes You'll Actually Use

Okay, let's get to the exciting part! These are the shortcodes I use all the time in my blog posts. I'll show you how each one works with real examples.

Let's begin with something easy: buttons. They are great for CTAs, download links, or just making your links stand out. This is what they look like:

Code:

<Button label="Primary Button" link="#" style="solid" />
<Button label="Outline Button" link="#" style="outline" />

Props:

  • label: The text displayed on the button
  • link: URL or path to navigate to
  • style: Either "solid" or "outline"
  • rel: SEO relationship (optional, defaults to "nofollow")

2. Notice Boxes - Get Your Reader's Attention

You know those bright callout boxes that are in documentation? Yes, those. They are quite helpful for drawing attention to essential things. There are four flavors:

Code:

<Notice type="note">Your content here</Notice>
<Notice type="tip">Your content here</Notice>
<Notice type="info">Your content here</Notice>
<Notice type="warning">Your content here</Notice>

3. Accordions - Hide the Details (Until They're Needed)

For FAQs and extensive explanations, I always use accordions. They maintain your page neat and tidy while yet giving you all the information. Click to make it bigger, then click again to make it smaller. Simple as that:

Code:

<Accordion title="Your Question Here">
  Your answer content here - can include markdown formatting!
</Accordion>

For Astro users: The client:load directive informs Astro to hydrate components when the site loads. In Nextplate (Next.js), this directive is not used—components are automatically interactive.

4. Tabs - When You Need Multiple Views

When you want to demonstrate the same item in different ways, like code examples for different frameworks (hint hint, Astro vs. Next.js), tabs are great. This is how they work:

Code:

<Tabs>
  <Tab name="Tab 1">Content for first tab</Tab>

  <Tab name="Tab 2">Content for second tab</Tab>
</Tabs>

5. Videos - Because Sometimes You Need to Show, Not Tell

YouTube Videos

Want to embed a YouTube video? Super easy:

Code:

<Youtube id="C0DPdy98e4c" title="Play:Youtube" />

Just grab the video ID from the YouTube URL (you know, the part after v=).

Custom Videos

Got your own videos? No problem:

Code:

<Video width="100%" src="path/to/your/video.mp4" />

Standard Markdown Elements

Of course, you've still got all your regular Markdown superpowers. Here's a quick refresher:

Headings

Use # for headings (H1 through H6):

# Heading 1
## Heading 2
### Heading 3

Text Formatting

  • Bold text with **text**
  • Italic text with *text*
  • Strikethrough with ~~text~~
  • Inline code with backticks

Lists

Ordered lists:

  1. First item
  2. Second item
  3. Third item

Unordered lists:

  • List item
  • List item
  • List item

Code Blocks

To highlight syntax, use three backticks with a language name:

function greet(name) {
  console.log(`Hello, ${name}!`);
}
def greet(name):
    print(f"Hello, {name}!")

Blockquotes

Use the > sign to make nice blockquotes. Great for making statements, testimonials, or key callouts stand out.

Tables

Create structured data with markdown tables:

FeatureAstroNext.js
MDX Support✅ Built-in✅ Via Plugin
Performance⚡ Excellent⚡ Excellent
Learning Curve📚 Easy📚 Moderate

Code:

| Feature | Astro | Next.js |
| : ------------- | : ----------: | ------------: |
| MDX Support | ✅ Built -in  | ✅ Via Plugin |
| Performance | ⚡ Excellent |  ⚡ Excellent |
| Learning Curve |   📚 Easy |   📚 Moderate |

Want to Build Your Own?

Once you get comfortable with these shortcodes, you might want to create your own. It's not that hard, really! Here's how to make the Button and Notice components:

Button Component (React/TSX)

Here's how the Button component is actually implemented in Nextplate:

import Link from "next/link";

const Button = ({
  label,
  link,
  style,
  rel,
}: {
  label: string;
  link: string;
  style?: string;
  rel?: string;
}) => {
  return (
    <Link
      href={link}
      target="_blank"
      rel={`noopener noreferrer ${
        rel ? (rel === "follow" ? "" : rel) : "nofollow"
      }`}
      className={`btn mb-4 me-4 no-underline hover:text-white dark:hover:text-black ${
        style === "outline" ? "btn-outline-primary" : "btn-primary"
      }`}
    >
      {label}
    </Link>
  );
};

export default Button;

Key differences from basic implementation:

  • Uses Next.js Link component for optimized navigation
  • Includes dark mode support with dark:hover:text-black
  • Adds utility classes for spacing and hover effects

Notice Component (React/TSX)

The Notice component in Nextplate includes custom SVG icons for each type:

import { humanize } from "@/lib/utils/textConverter";
import React from "react";

function Notice({
  type,
  children,
}: {
  type: string;
  children: React.ReactNode;
}) {
  return (
    <div className={`notice ${type}`}>
      <div className="notice-head">
        {/* SVG icons for tip, info, warning, and note types */}
        {type === "tip" ? (
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
            {/* Checkmark icon for tips */}
          </svg>
        ) : type === "info" ? (
          <svg width="20" height="20" viewBox="0 0 18 20" fill="none">
            {/* Info icon */}
          </svg>
        ) : type === "warning" ? (
          <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
            {/* Warning X icon */}
          </svg>
        ) : (
          <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
            {/* Note icon */}
          </svg>
        )}
        <p className="my-0 ml-1.5">{humanize(type)}</p>
      </div>
      <div className="notice-body">{children}</div>
    </div>
  );
}

export default Notice;

Note: The actual component includes full SVG paths for each icon type. It also uses the humanize utility to format the type name.

Some Tips I've Learned Along the Way

Here are some things I wish someone had told me when I started using shortcodes!

  1. Don't Go Overboard: Just because you can use shortcodes everywhere doesn't mean you should. Use them where they actually add value.
  2. Stay Consistent: Pick a style and stick with it. Your readers will appreciate the consistency.
  3. Think About Performance: In Astro, use client:load only when you really need immediate interactivity. For stuff below the fold, client:visible is your friend.
  4. Test on Mobile: I can't stress this enough. Always check how your shortcodes look on a phone. Always.
  5. Accessibility First: Make sure everything works with a keyboard and screen readers. It's not just good practice—it's the right thing to do.
  6. Keep a Cheat Sheet: I literally have a note with all my shortcode syntax. Saves me so much time!

Framework-Specific Stuff

Depending on whether you're using Astro or Next.js, there are a few framework-specific things worth knowing:

If You're Using Astro (or Astroplate)

  • Use client:load for interactive components that need immediate interactivity
  • Use client:visible for components that can wait until they're in viewport
  • Use client:idle for lower-priority interactive components
  • Keep most content as static HTML for optimal performance
  • Leverage Astro's partial hydration for better performance

If You're Using Next.js (or Nextplate)

  • Use dynamic imports for heavy components
  • Leverage Next.js Image component for optimized images
  • Consider using the App Router for better MDX integration
  • Use TypeScript for better component type safety
  • Implement proper SEO with metadata API

Conclusion

I understand that plain Markdown is easy and that's great. But occasionally your blog postings need a little more power. Shortcodes do just that. These components make your material much more interesting, whether you're using Astroplate for a quick Astro experience or Nextplate for the Next.js ecosystem.

Begin with little steps! Start by learning 2–3 shortcodes, and then add more as you get more comfortable.

And the best part is that what I've shown you today is only the tip of the iceberg. You may make custom components for just about anything, like image galleries, comparison tables, interactive demos, and more. There are no limits!

Happy blogging! 🚀