All Projects → shadcn → next-mdx

shadcn / next-mdx

Licence: MIT License
next-mdx provides a set of helper functions for fetching and rendering local MDX files. It handles relational data, supports custom components, is TypeScript ready and really fast.

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language
shell
77523 projects

Projects that are alternatives of or similar to next-mdx

blog
Tech Blog (moved to zenn.dev/hellorusk)
Stars: ✭ 29 (-83.52%)
Mutual labels:  nextjs, mdx
Mdx Docs
📝 Document and develop React components with MDX and Next.js
Stars: ✭ 412 (+134.09%)
Mutual labels:  nextjs, mdx
Personal-Site-Gourav.io
My personal site & blog made with NextJS, Typescript, MDX, Tailwind CSS. Deployed on Vercel : https://gourav.io
Stars: ✭ 64 (-63.64%)
Mutual labels:  nextjs, mdx
next-mdx-digital-garden-starter
An opinionated starting point for Digital Garden content authoring.
Stars: ✭ 50 (-71.59%)
Mutual labels:  nextjs, mdx
Leerob.io
✨ My portfolio built with Next.js, MDX, Tailwind CSS, and Vercel.
Stars: ✭ 1,369 (+677.84%)
Mutual labels:  nextjs, mdx
Egghead Next
The frontend for egghead.io.
Stars: ✭ 896 (+409.09%)
Mutual labels:  nextjs, mdx
Mastering Nextjs
A free video course for building static and server-side rendered applications with Next.js and React.
Stars: ✭ 256 (+45.45%)
Mutual labels:  nextjs, mdx
Tailwind Nextjs Starter Blog
This is a Next.js, Tailwind CSS blogging starter template. Comes out of the box configured with the latest technologies to make technical writing a breeze. Easily configurable and customizable. Perfect as a replacement to existing Jekyll and Hugo individual blogs.
Stars: ✭ 166 (-5.68%)
Mutual labels:  nextjs, mdx
Nextra
The Next.js Static Site Generator
Stars: ✭ 1,271 (+622.16%)
Mutual labels:  nextjs, mdx
Blog.hellorusk.net
Tech Blog
Stars: ✭ 28 (-84.09%)
Mutual labels:  nextjs, mdx
Mdx Embed
Embed 3rd party media content in MDX - no import required 🧽
Stars: ✭ 119 (-32.39%)
Mutual labels:  nextjs, mdx
Mdnext
The opinionated ecosystem for MDX powered NextJS apps for blogs, documentation, and more.
Stars: ✭ 242 (+37.5%)
Mutual labels:  nextjs, mdx
mdxpy
A simple, yet elegant MDX library for TM1
Stars: ✭ 19 (-89.2%)
Mutual labels:  mdx
react-ant-mobile
react+ant-mobile+Next.js 脚手架
Stars: ✭ 22 (-87.5%)
Mutual labels:  nextjs
medict
medict a cross platform dictionary application,support mdict (*.mdx/*.mdd) dictionary format
Stars: ✭ 154 (-12.5%)
Mutual labels:  mdx
personal-website-nextjs-chakra
A series I am making on YouTube where I build a personal website using next.js and chakra-ui. It includes a variety of other features such as mdx-remote blog pages, view counts with Google Firebase database and many more features.
Stars: ✭ 60 (-65.91%)
Mutual labels:  mdx
next-serverless
☁️ next-serverless deploys your next.js application to AWS Lambda with minimal or even no configuration.
Stars: ✭ 80 (-54.55%)
Mutual labels:  nextjs
server-authentication-next.js
No description or website provided.
Stars: ✭ 103 (-41.48%)
Mutual labels:  nextjs
mdict
node.js mdict (*.mdx, *.mdd) file reader
Stars: ✭ 39 (-77.84%)
Mutual labels:  mdx
gatsby-blog-mdx
A ready-to-use, customizable personal blog with minimalist design
Stars: ✭ 61 (-65.34%)
Mutual labels:  mdx

⚠️ This project has been retired. Consider using next-mdx-remote or mdx-bundler or nextra.

next-mdx

Test License PRs welcome! Follow @shadcn

next-mdx provides a set of helper functions for fetching and rendering local MDX files. It handles relational data, supports custom components, TypeScript ready and is really fast.

next-mdx is great for building mdx-powered pages, multi-user blogs, category pages..etc.

next-mdx-relational-data

Table of Contents

Demo

https://next-mdx-example.vercel.app

Quick Start

Learn how next-mdx works by looking at examples.

  1. Go to example-page
  2. Open next-mdx.json to see the sample configuration.
  3. Open pages/[[...slug]].tsx to see how MDX files are fetched and rendered.
  4. See types/index.d.ts for TypeScript.

Examples

Click to expand examples.

next-mdx.json
{
  "post": {
    "contentPath": "content/posts",
    "basePath": "/blog",
    "sortBy": "date",
    "sortOrder": "desc"
  },
}
pages/posts/[...slug].jsx
import { useHydrate } from "next-mdx/client"
import { getMdxNode, getMdxPaths } from "next-mdx/server"

export default function PostPage({ post }) {
const content = useHydrate(post)

  return (
    <article>
      <h1 variant="heading.title">{post.frontMatter.title}</h1>
      {post.frontMatter.excerpt ? (
        <p variant="text.lead" mt="4">
          {post.frontMatter.excerpt}
        </p>
      ) : null}
      <hr />
      {content}
    </article>
  )

}

export async function getStaticPaths() {
return {
paths: await getMdxPaths("post"),
fallback: false,
}
}

export async function getStaticProps(context) {
const post = await getMdxNode("post", context)

  if (!post) {
    return {
      notFound: true,
    }
  }

  return {
    props: {
      post,
    },
  }

}

Installation


npm i --save next-mdx

Configuration

Create a next-mdx.json file at the root of your project with the following:

{
  "post": {
    "contentPath": "content/posts",
    "basePath": "/blog",
    "sortBy": "date",
    "sortOrder": "desc"
  },
  "category": {
    "contentPath": "content/categories"
  }
}
  1. post, category and author keys are unique IDs used as references for your MDX types.
  2. contentPath (required) is where your MDX files are located.
  3. basePath (optional) is the path used for generating URLs.
  4. sortBy (optional, defaults to title) is the name of the frontMatter field used for sorting.
  5. sortOrder (optional, defaults to asc) is the sorting order.

Reference

next-mdx exposes 6 main helper functions:

  • getMdxPaths(sourceName: string)
  • getNode(sourceName, context)
  • getAllNodes(sourceName)
  • getMdxNode(sourceName, context, params)
  • getAllMdxNodes(sourceName, params)
  • useHydrate(node, params)

getMdxPaths

getMdxPaths(sourceName: string) returns an array of path params which can be passed directly to paths in getStaticPaths`.

  • sourceName is the unique ID defined in next-mdx.json

Example

// pages/blog/[...slug].js
import { getMdxPaths } from "next-mdx/server"

export async function getStaticPaths() {
  return {
    paths: await getMdxPaths("post"),
    fallback: false,
  }
}

getNode

getNode(sourceName, context) returns an MDXNode with frontMatter and relational data but without MDX data. This is really fast and cached.

Use this instead of getMdxNode if you are not rendering MDX content on a page.

  • sourceName is the unique ID defined in next-mdx.json
  • context is the context passed to getStaticProps or the slug as a string.

Example

// pages/blog/[...slug].js
import { getNode } from "next-mdx/server"

export async function getStaticProps(context) {
  const post = await getNode("post", context)

  if (!post) {
    return {
      notFound: true,
    }
  }

  return {
    props: {
      post,
    },
  }
}

getAllNodes

getAllNodes(sourceName) returns all MdxNode of the given type/source with frontMatter and relational data but without MDX data. This is also really fast and cached.

  • sourceName is the unique ID defined in next-mdx.json

Example

import { getAllNodes } from "next-mdx/server"

export async function getStaticProps() {
  return {
    props: {
      posts: await getAllNodes("post"),
    },
  }
}

getMdxNode

getMdxNode(sourceName, context, params) returns an MDXNode.

  • sourceName is the unique ID defined in next-mdx.json
  • context is the context passed to getStaticProps or the slug as a string.
  • params:
{
  components?: MdxRemote.Components
  scope?: Record<string, unknown>
  provider?: MdxRemote.Provider
  mdxOptions?: {
    remarkPlugins?: Pluggable[]
    rehypePlugins?: Pluggable[]
    hastPlugins?: Pluggable[]
    compilers?: Compiler[]
    filepath?: string
  }
}

Example

// pages/blog/[...slug].js
import { getMdxNode } from "next-mdx/server"

export async function getStaticProps(context) {
  const post = await getMdxNode("post", context)

  if (!post) {
    return {
      notFound: true,
    }
  }

  return {
    props: {
      post,
    },
  }
}

getAllMdxNodes

getAllMdxNodes(sourceName, params) returns all MdxNode of the given type/source.

  • sourceName is the unique ID defined in next-mdx.json
  • params:
{
  components?: { name: React.Component },
  scope?: {},
  provider?: { component: React.Component, props: Record<string, unknown> },
  mdxOptions: {
    remarkPlugins: [],
    rehypePlugins: [],
    hastPlugins: [],
    compilers: [],
  }
}

Example

import { getAllMdxNodes } from "next-mdx/server"

export async function getStaticProps() {
  const posts = await getAllMdxNodes("post")

  return {
    props: {
      posts: posts.filter((post) => post.frontMatter.featured),
    },
  }
}

useHydrate

useHydrate(node, params) is used on the client side for hydrating static content.

  • node is the MdxNode object
  • params:
{
  components?: { name: React.Component },
  provider?: { component: React.Component, props: Record<string, unknown> }
}

Example

import { useHydrate } from "next-mdx/client"

export default function PostPage({ post }) {
  const content = useHydrate(post)

  return (
    <div>
      <h1>{post.frontMatter.title}</h1>

      {content}
    </div>
  )
}

getAllNodes vs getAllMdxNodes

Use getAllNodes when you need nodes without the MDX content. It is backed by a cache and is really fast. This is handy when you need a list of nodes (example post teasers) and you're not using the MDX content.

MDX Components

To use components inside MDX files, you need to pass the components to both getMdxNode/getAllMdxNodes and useHydrate.

Example

import { getMdxNode } from "next-mdx/server"
import { useHydrate } from "next-mdx/client"

export function Alert({ text }) {
  return <p>{text}</p>
}

export default function PostPage({ post }) {
  const content = useHydrate(post, {
    components: {
      Alert,
    },
  })

  return (
    <div>
      <h1>{post.frontMatter.title}</h1>

      {content}
    </div>
  )
}

export async function getStaticProps(context) {
  const post = await getMdxNode("post", context, {
    components: {
      Alert,
    },
  })

  return {
    props: {
      post,
    },
  }
}

MDX Options

MDX options can be passed as params to both getMdxNode(sourceName, context, params) and getAllMdxNodes(sourceName, params) where params takes the shape of:

export interface MdxParams {
  components?: MdxRemote.Components
  scope?: Record<string, unknown>
  provider?: MdxRemote.Provider
  mdxOptions?: {
    remarkPlugins?: Pluggable[]
    rehypePlugins?: Pluggable[]
    hastPlugins?: Pluggable[]
    compilers?: Compiler[]
    filepath?: string
  }
}

Relational Data

When retrieving nodes with getMdxNode or getAllMdxNodes, next-mdx will automatically infer relational data from frontMatter keys.

Convention

  1. The frontMatter field name must be the same as the key defined in next-mdx.json
  2. The frontMatter field must be an array of values.

Example

Given the following MDX files.

.
└── content
    ├── categories
    │   └── category-a.mdx
    │   └── category-b.mdx
    └── posts:
        └── example-post.mdx

In example-post you can reference related categories using the following:

---
title: Example Post
category:
  - category-a
---

You can then access the categories as follows:

const post = getMdxNode("post", context)

// post.relationships.category

Plugins

TypeScript

Define your node types as follows:

interface Post extends MdxNode<FrontMatterFields> {}

Example

import { MdxNode } from "next-mdx/server"

interface Category
  extends MdxNode<{
    name: string
  }> {}

interface Post
  extends MdxNode<{
    title: string
    excerpt?: string
    category?: string[]
  }> {
  relationships?: {
    category: Category[]
  }
}

You can then use Post as the return type for getNode, getAllNodes, getMdxNode and getAllMdxNode:

const post = await getMdxNode<Post>("post", context)

const posts = await getAllNodes<Post>("post")

License

Licensed under the MIT license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].