Code Monkey home page Code Monkey logo

next-mdx-remote's Introduction

next-mdx-remote


Installation

npm install next-mdx-remote

If using with Turbopack, you'll need to add the following to your next.config.js until this issue is resolved:

const nextConfig = {
+  transpilePackages: ['next-mdx-remote'],
}

Examples

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

import Test from '../components/test'

const components = { Test }

export default function TestPage({ source }) {
  return (
    <div className="wrapper">
      <MDXRemote {...source} components={components} />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = 'Some **mdx** text, with a component <Test />'
  const mdxSource = await serialize(source)
  return { props: { source: mdxSource } }
}

While it may seem strange to see these two in the same file, this is one of the cool things about Next.js -- getStaticProps and TestPage, while appearing in the same file, run in two different places. Ultimately your browser bundle will not include getStaticProps at all, or any of the functions it uses only on the server, so serialize will be removed from the browser bundle entirely.

IMPORTANT: Be very careful about putting any next-mdx-remote code into a separate "utilities" file. Doing so will likely cause issues with Next.js' code splitting abilities - it must be able to cleanly determine what is used only on the server side and what should be left in the client bundle. If you put next-mdx-remote code into an external utilities file and something is broken, remove it and start from the simple example above before filing an issue.

Additional Examples

Parsing Frontmatter

Markdown in general is often paired with frontmatter, and normally this means adding some extra custom processing to the way markdown is handled. To address this, next-mdx-remote comes with optional parsing of frontmatter, which can be enabled by passing parseFrontmatter: true to serialize.

Here's what that looks like:

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

import Test from '../components/test'

const components = { Test }

export default function TestPage({ mdxSource }) {
  return (
    <div className="wrapper">
      <h1>{mdxSource.frontmatter.title}</h1>
      <MDXRemote {...mdxSource} components={components} />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = `---
title: Test
---

Some **mdx** text, with a component <Test name={frontmatter.title}/>
  `

  const mdxSource = await serialize(source, { parseFrontmatter: true })
  return { props: { mdxSource } }
}

vfile-matter is used to parse the frontmatter.

Passing custom data to a component with `scope`

<MDXRemote /> accepts a scope prop, which makes all of the values available for use in your MDX.

Each key/value pair in the scope argument will be exposed as a javascript variable. So, for example, you could imagine if you had a scope like { foo: 'bar' }, it would be interpreted as const foo = 'bar'.

This specifically means that you need to make sure that key names in your scope argument are valid javascript variable names. For example, passing in { 'my-variable-name': 'bar' } would generate an error, because the key name is not a valid javascript variable name.

It's also important to note that scope variables must be consumed as arguments to a component, they cannot be rendered in the middle of text. This is shown in the example below.

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

import Test from '../components/test'

const components = { Test }
const data = { product: 'next' }

export default function TestPage({ source }) {
  return (
    <div className="wrapper">
      <MDXRemote {...source} components={components} scope={data} />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source =
    'Some **mdx** text, with a component using a scope variable <Test product={product} />'
  const mdxSource = await serialize(source)
  return { props: { source: mdxSource } }
}
Passing `scope` into the `serialize` function instead

You can also pass custom data into serialize, which will then pass the value through and make it available from its result. By spreading the result from source into <MDXRemote />, the data will be made available.

Note that any scope values passed into serialize need to be serializable, meaning passing functions or components is not possible. Additionally, any key named in the scope argument must be valid javascript variable names. If you need to pass custom scope that is not serializable, you can pass scope directly to <MDXRemote /> where it's rendered. There is an example of how to do this above this section.

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

import Test from '../components/test'

const components = { Test }
const data = { product: 'next' }

export default function TestPage({ source }) {
  return (
    <div className="wrapper">
      <MDXRemote {...source} components={components} />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source =
    'Some **mdx** text, with a component <Test product={product} />'
  const mdxSource = await serialize(source, { scope: data })
  return { props: { source: mdxSource } }
}
Custom components from MDXProvider

If you want to make components available to any <MDXRemote /> being rendered in your application, you can use <MDXProvider /> from @mdx-js/react.

// pages/_app.jsx
import { MDXProvider } from '@mdx-js/react'

import Test from '../components/test'

const components = { Test }

export default function MyApp({ Component, pageProps }) {
  return (
    <MDXProvider components={components}>
      <Component {...pageProps} />
    </MDXProvider>
  )
}
// pages/test.jsx
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

export default function TestPage({ source }) {
  return (
    <div className="wrapper">
      <MDXRemote {...source} />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = 'Some **mdx** text, with a component <Test />'
  const mdxSource = await serialize(source)
  return { props: { source: mdxSource } }
}
Component names with dot (e.g. motion.div)

Component names that contain a dot (.), such as those from framer-motion, can be rendered the same way as other custom components, just pass motion in your components object.

import { motion } from 'framer-motion'

import { MDXProvider } from '@mdx-js/react'
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

export default function TestPage({ source }) {
  return (
    <div className="wrapper">
      <MDXRemote {...source} components={{ motion }} />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = `Some **mdx** text, with a component:

<motion.div animate={{ x: 100 }} />`
  const mdxSource = await serialize(source)
  return { props: { source: mdxSource } }
}
Lazy hydration

Lazy hydration defers hydration of the components on the client. This is an optimization technique to improve the initial load of your application, but may introduce unexpected delays in interactivity for any dynamic content within your MDX content.

Note: this will add an additional wrapping div around your rendered MDX, which is necessary to avoid hydration mismatches during render.

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

import Test from '../components/test'

const components = { Test }

export default function TestPage({ source }) {
  return (
    <div className="wrapper">
      <MDXRemote {...source} components={components} lazy />
    </div>
  )
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = 'Some **mdx** text, with a component <Test />'
  const mdxSource = await serialize(source)
  return { props: { source: mdxSource } }
}

APIs

This library exposes a function and a component, serialize and <MDXRemote />. These two are purposefully isolated into their own files -- serialize is intended to be run server-side, so within getStaticProps, which runs on the server/at build time. <MDXRemote /> on the other hand is intended to be run on the client side, in the browser.

  • serialize(source: string, { mdxOptions?: object, scope?: object, parseFrontmatter?: boolean })

    serialize consumes a string of MDX. It can also optionally be passed options which are passed directly to MDX, and a scope object that can be included in the MDX scope. The function returns an object that is intended to be passed into <MDXRemote /> directly.

    serialize(
      // Raw MDX contents as a string
      '# hello, world',
      // Optional parameters
      {
        // made available to the arguments of any custom MDX component
        scope: {},
        // MDX's available options, see the MDX docs for more info.
        // https://mdxjs.com/packages/mdx/#compilefile-options
        mdxOptions: {
          remarkPlugins: [],
          rehypePlugins: [],
          format: 'mdx',
        },
        // Indicates whether or not to parse the frontmatter from the MDX source
        parseFrontmatter: false,
      }
    )

    Visit https://mdxjs.com/packages/mdx/#compilefile-options for available mdxOptions.

  • <MDXRemote compiledSource={string} components?={object} scope?={object} lazy?={boolean} />

    <MDXRemote /> consumes the output of serialize as well as an optional components argument. Its result can be rendered directly into your component. To defer hydration of the content and immediately serve the static markup, pass the lazy prop.

    <MDXRemote {...source} components={components} />

Replacing default components

Rendering will use MDXProvider under the hood. This means you can replace HTML tags by custom components. Those components are listed in MDXJS Table of components.

An example use case is rendering the content with your preferred styling library.

import { Typography } from "@material-ui/core";

const components = { Test, h2: (props) => <Typography variant="h2" {...props} /> }
...

If you prefer, you can also wrap your entire application in an <MDXProvider /> instead of passing your components directly to <MDXRemote />. See the example above.

Note: th/td won't work because of the "/" in the component name.

Background & Theory

There isn't really a good default way to load MDX files in a Next.js app. Previously, we wrote next-mdx-enhanced in order to be able to render your MDX files into layouts and import their front matter to create index pages.

This workflow from next-mdx-enhanced was fine, but introduced a few limitations that we have removed with next-mdx-remote:

  • The file content must be local. You cannot store MDX files in another repo, a database, etc. For a large enough operation, there will end up being a split between those authoring content and those working on presentation of the content. Overlapping these two concerns in the same repo makes a more difficult workflow for everyone.
  • You are bound to filesystem-based routing. Your pages are generated with urls according to their locations. Or maybe you remap them using exportPathMap, which creates confusion for authors. Regardless, moving pages around in any way breaks things -- either the page's url or your exportPathMap configuration.
  • You will end up running into performance issues. Webpack is a JavaScript bundler, forcing it to load hundreds/thousands of pages of text content will blow out your memory requirements. Webpack stores each page as a distinct object with a large amount of metadata. One of our implementations with a couple hundred pages hit more than 8GB of memory required to compile the site. Builds took more than 25 minutes.
  • You will be limited in the ways you are able to structure relational data. Organizing content into dynamic, related categories is difficult when your entire data structure is front matter parsed into javascript objects and held in memory.

So, next-mdx-remote changes the entire pattern so that you load your MDX content not through an import, but rather through getStaticProps or getServerProps -- you know, the same way you would load any other data. The library provides the tools to serialize and hydrate the MDX content in a manner that is performant. This removes all of the limitations listed above, and does so at a significantly lower cost -- next-mdx-enhanced is a very heavy library with a lot of custom logic and some annoying limitations. Our informal testing has shown build times reduced by 50% or more.

Since this project was initially created, Kent C. Dodds has made a similar project, mdx-bundler. This library supports imports and exports within a MDX file (as long as you manually read each imported file and pass its contents) and automatically processes frontmatter. If you have a lot of files that all import and use different components, you may benefit from using mdx-bundler, as next-mdx-remote currently only allows components to be imported and made available across all pages. It's important to note that this functionality comes with a cost though - mdx-bundler's output is at least 400% larger than the output from next-mdx-remote for basic markdown content.

How Can I Build A Blog With This?

Data has shown that 99% of use cases for all developer tooling are building unnecessarily complex personal blogs. Just kidding. But seriously, if you are trying to build a blog for personal or small business use, consider just using normal HTML and CSS. You definitely do not need to be using a heavy full-stack JavaScript framework to make a simple blog. You'll thank yourself later when you return to make an update in a couple years and there haven't been 10 breaking releases to all of your dependencies.

If you really insist though, check out our official Next.js example implementation. ๐Ÿ’–

Caveats

Environment Targets

The code generated by next-mdx-remote, which is used to actually render the MDX targets browsers with module support. If you need to support older browsers, consider transpiling the compiledSource output from serialize.

import / export

import and export statements cannot be used inside an MDX file. If you need to use components in your MDX files, they should be provided as a prop to <MDXRemote />.

Hopefully this makes sense, since in order to work, imports must be relative to a file path, and this library allows content to be loaded from anywhere, rather than only loading local content from a set file path. As for exports, the MDX content is treated as data, not a module, so there is no way for us to access any value which may be exported from the MDX passed to next-mdx-remote.

Security

This library evaluates a string of JavaScript on the client side, which is how it MDXRemotes the MDX content. Evaluating a string into javascript can be a dangerous practice if not done carefully, as it can enable XSS attacks. It's important to make sure that you are only passing the mdxSource input generated by the serialize function to <MDXRemote />, as instructed in the documentation. Do not pass user input into <MDXRemote />.

If you have a CSP on your website that disallows code evaluation via eval or new Function(), you will need to loosen that restriction in order to utilize next-mdx-remote, which can be done using unsafe-eval.

TypeScript

This project does include native types for TypeScript use. Both serialize and <MDXRemote /> have types normally as you'd expect, and the library also exports a type which you can use to type the result of getStaticProps.

  • MDXRemoteSerializeResult<TScope = Record<string, unknown>>: Represents the return value of serialize. The TScope generic type can be passed to represent the type of the scoped data you pass in.

Below is an example of a simple implementation in TypeScript. You may not need to implement the types exactly in this way for every configuration of TypeScript - this example is just a demonstration of where the types could be applied if needed.

import type { GetStaticProps } from 'next'
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote, type MDXRemoteSerializeResult } from 'next-mdx-remote'
import ExampleComponent from './example'

const components = { ExampleComponent }

interface Props {
  mdxSource: MDXRemoteSerializeResult
}

export default function ExamplePage({ mdxSource }: Props) {
  return (
    <div>
      <MDXRemote {...mdxSource} components={components} />
    </div>
  )
}

export const getStaticProps: GetStaticProps<{
  mdxSource: MDXRemoteSerializeResult
}> = async () => {
  const mdxSource = await serialize('some *mdx* content: <ExampleComponent />')
  return { props: { mdxSource } }
}

React Server Components (RSC) & Next.js app Directory Support

Usage of next-mdx-remote within server components, and specifically within Next.js's app directory, is supported by importing from next-mdx-remote/rsc. Previously, the serialization and render steps were separate, but going forward RSC makes this separation unnecessary.

Some noteworthy differences:

  • <MDXRemote /> now accepts a source prop, instead of accepting the serialized output from next-mdx-remote/serialize
  • Custom components can no longer be provided by using the MDXProvider context from @mdx-js/react, as RSC does not support React Context
  • To access frontmatter outside of your MDX when passing parseFrontmatter: true, use the compileMdx method exposed from next-mdx-remote/rsc
  • The lazy prop is no longer supported, as the rendering happens on the server
  • <MDXRemote /> must be rendered on the server, as it is now an async component. Client components can be rendered as part of the MDX markup

For more information on RSC, check out the Next.js documentation.

Examples

Assuming usage in a Next.js 13+ application using the app directory.

Basic

import { MDXRemote } from 'next-mdx-remote/rsc'

// app/page.js
export default function Home() {
  return (
    <MDXRemote
      source={`# Hello World

      This is from Server Components!
      `}
    />
  )
}

Loading state

import { MDXRemote } from 'next-mdx-remote/rsc'

// app/page.js
export default function Home() {
  return (
    // Ideally this loading spinner would ensure there is no layout shift,
    // this is an example for how to provide such a loading spinner.
    // In Next.js you can also use `loading.js` for this.
    <Suspense fallback={<>Loading...</>}>
      <MDXRemote
        source={`# Hello World

        This is from Server Components!
        `}
      />
    </Suspense>
  )
}

Custom Components

// components/mdx-remote.js
import { MDXRemote } from 'next-mdx-remote/rsc'

const components = {
  h1: (props) => (
    <h1 {...props} className="large-text">
      {props.children}
    </h1>
  ),
}

export function CustomMDX(props) {
  return (
    <MDXRemote
      {...props}
      components={{ ...components, ...(props.components || {}) }}
    />
  )
}
// app/page.js
import { CustomMDX } from '../components/mdx-remote'

export default function Home() {
  return (
    <CustomMDX
      // h1 now renders with `large-text` className
      source={`# Hello World
      This is from Server Components!
    `}
    />
  )
}

Access Frontmatter outside of MDX

// app/page.js
import { compileMDX } from 'next-mdx-remote/rsc'

export default async function Home() {
  // Optionally provide a type for your frontmatter object
  const { content, frontmatter } = await compileMDX<{ title: string }>({
    source: `---
title: RSC Frontmatter Example
---
# Hello World
This is from Server Components!
`,
    options: { parseFrontmatter: true },
  })
  return (
    <>
      <h1>{frontmatter.title}</h1>
      {content}
    </>
  )
}

Alternatives

next-mdx-remote is opinionated in what features it supports. If you need additional features not provided by next-mdx-remote, here are a few alternatives to consider:

You Might Not Need next-mdx-remote

If you're using React Server Components and just trying to use basic MDX with custom components, you don't need anything other than the core MDX library.

import { compile, run } from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime'
import ClientComponent from './components/client'

// MDX can be retrieved from anywhere, such as a file or a database.
const mdxSource = `# Hello, world!
<ClientComponent />
`

export default async function Page() {
  // Compile the MDX source code to a function body
  const code = String(
    await compile(mdxSource, { outputFormat: 'function-body' })
  )
  // You can then either run the code on the server, generating a server
  // component, or you can pass the string to a client component for
  // final rendering.

  // Run the compiled code with the runtime and get the default export
  const { default: MDXContent } = await run(code, {
    ...runtime,
    baseUrl: import.meta.url,
  })

  // Render the MDX content, supplying the ClientComponent as a component
  return <MDXContent components={{ ClientComponent }} />
}

You can also simplify this approach with evaluate, which compiles and runs code in a single call if you don't plan on passing the compiled string to a database or client component.

import { evaluate } from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime'
import ClientComponent from './components/client'

// MDX can be retrieved from anywhere, such as a file or a database.
const mdxSource = `
export const title = "MDX Export Demo";

# Hello, world!
<ClientComponent />

export function MDXDefinedComponent() {
  return <p>MDX-defined component</p>;
}
`

export default async function Page() {
  // Run the compiled code
  const {
    default: MDXContent,
    MDXDefinedComponent,
    ...rest
  } = await evaluate(mdxSource, runtime)

  console.log(rest) // logs { title: 'MDX Export Demo' }

  // Render the MDX content, supplying the ClientComponent as a component, and
  // the exported MDXDefinedComponent.
  return (
    <>
      <MDXContent components={{ ClientComponent }} />
      <MDXDefinedComponent />
    </>
  )
}

License

Mozilla Public License Version 2.0

next-mdx-remote's People

Contributors

bogdansoare avatar brianespinosa avatar chrisweb avatar chunfeilung avatar cknitt avatar dannyphillips avatar dependabot[bot] avatar devrsi0n avatar dstaley avatar eric-burel avatar hashibot-web avatar hashicorp-copywrite[bot] avatar hocdoc avatar jakejarvis avatar jerrygreen avatar jescalan avatar jkjustjoshing avatar joemckenney avatar marcofranssen avatar matthewoates avatar matthijsmud avatar max-pod avatar navidadelpour avatar nwalters512 avatar pabloszx avatar pbteja1998 avatar rafaelalmeidatk avatar rebootgg avatar thiskevinwang avatar timneutkens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

next-mdx-remote's Issues

Question: Possible to return the contents as an array of elements?

I was originally using the MDX library with Next, until I wanted to use getStaticProps, which this package lets me do.

However, before I was able to map over the content nodes before to generate my own custom Table of Contents, something I'm not able to do here.

Is there a way to transform the content into an array of nodes, instead of just the compiled string of HTML? Ideally I want to pull out and create an array of all H2 + H3 elements ... without regex-ing them myself

How to go about managing post related assets like images, OG image etc

I like the structure where post markdown and its assets are stored in same folder for easier management instead of creating post mdx files and then storing all posts assets in a different folder. So, is it possible to keep post and its assets in their respective folder and still access them when using this library?
For example:

project:
   /posts
     /my-first-post
        -index.mdx
        -first-post.png
        -og.png
    /my-second-post
        -index.mdx
        -second-post.png
        -og.png

Usage clarification

Hi I've read through the readme, can you help me out with a couple of questions please?

  1. Is it correct that the app must be rebuilt if the md source content is updated?

  2. Because of getStaticProps I need to have the string available at the page level.
    If I'm making a query from inside a component is it possible to use next-mdx-remote?

Example of Markdown that has HTML attributes

I would love to include an example on the README to include what I feel like is something pretty common having raw html in your markdown.

Include an example error like
Error: The style prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.

Then show the plugins for remark and/or rehype that would fix this type of issue.

renderToString(postData.post_content, {
        mdxOptions: {
          remarkPlugins: [parse, remark2react],
        },
      })

Example of gross content, that with style tags that gets converted great.

    "content": "\n<iframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"\" frameborder=\"0\" height=\"360\" src=\"https://www.youtube.com/embed/kUM8Zcb369U?feature=oembed\" title=\"Amplify with Nader Dabit | Purrfect.dev\" width=\"640\"></iframe>\n\n<iframe frameborder=\"0\" height=\"180px\" scrolling=\"no\" src=\"https://anchor.fm/purrfect-dev/embed/episodes/0-10---Amplify-with-Nader-Dabit-edvjpb\" style=\"width: 100%; height: 180px;\" width=\"100%\"></iframe>\n\n**AWS Amplify**\n\n**Guest Details**\n-----------------\n\n### **Nader Dabit**\n\n**Senior Developer Advocate at Amazon Web Services  \nWeb and mobile developer specializing in cross-platform &amp; cloud-enabled application development.**\n\n#### **Links**\n\n[<u>**https://www.linkedin.com/in/naderdabit/**</u>](https://www.linkedin.com/in/naderdabit/)\n\n[<u>**https://twitter.com/dabit3**</u>  ](https://twitter.com/dabit3)[<u>**https://dev.to/dabit3/**</u>](https://dev.to/dabit3/)\n\n**Questions**\n-------------\n\n**What is AWS Amplify?**\n\n**Do you see more people using AppSync vs. API Management?**\n\n**We have had a hard time differentiating DynamoDB Scan/Query from what actually gets filtered through AppSync, any perspectives on how to avoid common issues with this?**\n\n**Is there a way to debug using `amplify mock function` in VSCode with 4.18.0??**\n\n**Do you recommend using Cloudfront to serve S3 images for your Amplify hosted site?**\n\n**Future**\n----------\n\n**I know the video plugin is coming along, are there any other cool features coming out for Amplify that people should know about?**\n\n**Purrfect Picks**\n------------------\n\n### **Nader**\n\n<https://www.amazon.com/Flash-Boys-Wall-Street-Revolt/dp/0393351599>  \n<https://www.amazon.com/Autobiography-Malcolm-Told-Alex-Haley/dp/0345350685>  \n<https://www.youtube.com/channel/UCv75sKQFFIenWHrprnrR9aA>\n\n### **Alex**\n\n**Amplify Video Tutorial for VOD**\n\n[<u>**https://github.com/awslabs/unicornflix**</u>  ](https://github.com/awslabs/unicornflix)\n\nโ€”\n\nSend in a voice message: <https://anchor.fm/purrfect-dev/message>"

Why is rendered output wrapped in a <span>?

First of all, thanks for making next-mdx-remote; this makes my life so much easier!

One thing I did notice is that hydrate.js wraps the renderedOutput in a span element:

React.createElement('span', {
  dangerouslySetInnerHTML: {
    __html: renderedOutput,
  },
})

Because the renderedOutput may include block-level elements โ€“ which are technically not allowed within inline elements like span โ€“ the resulting HTML will often be invalid. As far as I can tell this does not cause any actual issues, but it does make me wonder whether this is a bug or a feature. ๐Ÿ˜„

ReferenceError: title is not defined

I've tried out the second example , the one parsing the variable title with gray matter.
It raises this error:
image

It's not picking up title from front-matter

I've replicated the error in this repo (in testpage2.js )
https://github.com/rodbs/nextjs-mdx-remote

Btw, the mdx source has to be within template literals? what about if I get it from a db? how a parse it?

  const source = `
---
title: Test
---

Some **mdx** text, with a component <Test name={title}/>
`

Thanks

Add Example to Next.js Repo

Hi there! I recently moved from next-mdx-enhanced to next-mdx-remote and saw a huge gain in build performance. Thanks for this work!

I wanted to contribute an example for this repo into the Next.js repo, and opened a pull request here: vercel/next.js#16613

Please feel free to let me know if you have feedback on the proposed example.

Question: Page flashing

This package together with next-remote-watch are great for writing mdx documents. However, recently I'm seeing the documents take a noticeable time interval to refresh, and there's a white flashing in between. A couple of weeks ago I didn't notice these problems.

Could anything be done?

P.S. Also, after a page refreshes it always starts from the top and it's a bit tedious for me to have to scroll to the place where I was before. Is there a way to solve this as well?

Thanks!

dynamic component is not rendering on initial render

enviroment:

  • latest next-mdx-remote
  • latest nextjs
  • site is statically generated
const components = (title?: string) => ({
  AwesomeCutScenes: dynamic(
    () =>
      import("awesome-react-cutscenes").then(
        (module) => module.CutScenes
        // TODO not sure why it doesn't compile w/o any
       // (probably a nextjs typing issue)
      ) as any,
    {
      ssr: false,
      loading() {
        return <ComicPlaceholder>{title}</ComicPlaceholder>;
      },
    }
  ),
});

Can babel dependencies be moved to devDependencies?

Hey,

Just a quick question, are these dependencies
"@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", "@babel/preset-react": "^7.10.4",
needed at runtime? Would it be possible to move them to devDependencies instead?

Context is, it's increasing our bundle sizes by a few megabytes by pulling these babel deps, I believe these are typically not needed outside of devDeps. Just want to confirm.

Thank you!

There's no rehydration after client page transition

Hello!

I'm loving the library's concept, I really really like it!

For context I'm trying to create a small presentation. This is my [slide].jsx component:

// pages/[slide].jsx

import { join } from 'path';
import fs from 'fs';
import renderToString from 'next-mdx-remote/render-to-string';
import hydrate from 'next-mdx-remote/hydrate';
import matter from 'gray-matter';

import Intro from '../components/Intro';

const postsDirectory = join(process.cwd(), '/components/slides/');

function getFileNames() {
  return fs.readdirSync(postsDirectory);
}

function getSlug(slug) {
  const initialSlug = slug.replace(/\.mdx$/, '');
  const cleanSlug = initialSlug.replace(/slide-/, '');

  return cleanSlug;
}

function getSlidesSlugs() {
  const fileNames = getFileNames();
  const slugs = fileNames.map((fileName) => getSlug(fileName));
  const filteredSlugs = slugs.slice(1);
  // sort posts by date in descending order
  return filteredSlugs;
}

const components = { Intro };

const Slide = ({ mdxSource, frontMatter }) => {
  const content = hydrate(mdxSource, components);
  console.log({ mdxSource, content });

  return (
    <div>
      <h1 className="text-white">{frontMatter.title}</h1>
      {content}
    </div>
  );
};

export async function getStaticProps({ params }) {
  const fullPath = join(postsDirectory, `slide-${params.slide}.mdx`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');
  const { content, data } = matter(fileContents);
  const mdxSource = await renderToString(content, components);
  return { props: { mdxSource, frontMatter: data } };
}

export async function getStaticPaths() {
  const slugs = getSlidesSlugs();
  return {
    paths: slugs.map((slug) => {
      return {
        params: {
          slide: slug.toString(),
        },
      };
    }),
    fallback: false,
  };
}

export default Slide;

As you can see in the Slide function component definition, I'm console logging both mdxSource and content.

This is the result from the console.

  • There are 3 renders:

Screen Shot 2020-05-25 at 12 42 10 AM

  • The first and second render show both values correctly:

Screen Shot 2020-05-25 at 12 42 16 AM

  • But the third doesn't have the props for content as initially:

Screen Shot 2020-05-25 at 12 44 09 AM

Then finally when I click on a <Link/> to go to the next slide this happens (this is a gif, give it a couple of seconds, forgot to remove the initial idle time ๐Ÿ˜„):

Screen Recording 2020-05-25 at 12 47 26 AM

The gif shows:

  • On the device top left corner you can see a slide counter which increases from 3 to 4 when the click is pressed, this signals the page transition (this is from the frontMatter prop)
  • But the actual content does not change
  • mdxSource did update correctly as it's showing a different piece of content on console, this also comes as a prop from getStaticProps -- so I think there's no problem there
  • But content, calculated with hydrate, lost its props (as seen in the console.log), and so my guess is this is why the content did not update correctly on client page transition.

Am I using the library correctly? Not sure if this is outside of the current scope or if I should be using it differently.

As a note I'd like to point out the problem disappears if instead of using a <Link/> component I simply use an <a/> tag for the slide transitions. But with the <Link/> component there's quite a few good things that happen, so it would be really great if next-mdx-remote would work the <Link/> component.

I'll share the full repo as soon as I can, but this is the best I can do for now.

Any guidance would be highly appreciated! Thanks in advance

Question: using export in mdx

Hi there,

Thank you so much to all the maintainers for such a great library. I have a question about defining components within the mdx. I have read the caveat about not importing components within the mdx but that seems like a different issue (I apologise if it isn't!)

I am wondering if this is just something that probably won't work ever given the constraints of the package, or if there is something I can do.

Thank you so much,

Lydia

import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'

const source = `
# Test

export const FancyButton = ({ text = "Press" }) => {
  return (
    <button onClick={() => console.log}>{text}</button>
  )
}

<FancyButton text="Press me" />
`

export default function TestPage({ source }) {
  const content = hydrate(source)

  return <div>{content}</div>
}

export async function getStaticProps() {
  const mdxSource = await renderToString(source)

  return { props: { source: mdxSource } }
}

image

Elaborate on renderToString without hydration in readme

Hey there, thanks so much for writing this library! I've long been frustrated with the limitations of using MDX with Next, so I'm really glad to discover that someone else also thought there must be a better way (and had the capacity to act on it :) !)

Currently the readme ends on a somewhat ambiguous note:

It's also worth noting that you do not have to use hydrate on the client side, but without it, you will get a server-rendered result, meaning no ability to react to user input, etc.

To clarify, does this mean the output of renderToString can be passed directly to dangerouslySetInnerHTML? For my part there are plenty of use-cases where I'd like the componentization provided by MDX for components that are non-interactive (eg. simple layout elements), and I can't imagine I'm alone in this...

Spread syntax on hydrate function breaks on Safari 11

Hi, I'm switched from a custom made mdx parser to your solution and it's very useful thanks!

However I notice that my site breaks on safari 11 because in the hydrate function you're using the spread syntax and I have this console error: Unexpected token '...'. Expected a property name.

I can't understand why this isn't transpiled by babel :\

Feature request: TypeScript definition

Do you have any plans to migrate to TypeScript?

Whether converting source code to TS or writing definition file, I can help on that so let me know ๐Ÿ˜‰

next/link links with hash not working

I'm trying to render markdown docs using mdx and next-mdx-remote. I replaced standard <a> tags in docs with next/link for smoother navigation. It works, but if the link has fragment link, e. g. docs/page#about, then navigation to anchor on page load didn't work. If I just render renderOutput to dangerouslySetInnerHTML={{ __html: mdx.renderedOutput }}, then everything works correctly.

My guess is that bug happens because navigation to hash hapens at the time then hydrate is replacing the page content and there are no actual anchor on the page.

Replacing standard markdown elements doesn't work

If I set the components to target standard markdown html elements, like h1, img etc.

e.g.

const components = {
  h1: (props) => <h1 style={{ color: 'tomato' }} {...props} />,
}

Then the static files are correct.

But, on page load in the browser. I think the component assignment is ignored.

With my example I'll see a red h1 for a second then black.

Html source has this in it <h1 style="color:tomato">Games</h1> ... mdx(\"h1\", null, \"Games\"),

Expected behaviour - consistent components across static and in browser.

Thanks - Davey

TypeError: Cannot convert undefined or null to object

I'm replicating the example you've posted in my project

export async function getStaticProps() {
  const { posts } = await getAllPosts();
  //   console.log(posts);
  const source = 'Some **mdx** text, with a component  ';
  const mdxSource = await renderToString(source, components);
  return {
    props: {
      posts,
      mdxSource
    },
    unstable_revalidate: 1
  };
}

I've tried different ways but I'm always getting this error. Any ideas? is it a bug?
image

TypeError: Cannot convert undefined or null to object
  at Function.keys (<anonymous>)
  at C:\igls\igls-next-firebase\node_modules\next-mdx-remote\render-to-string.js:43:19   
  at processTicksAndRejections (internal/process/task_queues.js:97:5)
  at async getStaticProps (C:\igls\igls-next-firebase\.next\server\static\development\pages\blog.js:2048:21)
  at async renderToHTML (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\render.js:27:109)
  at async C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:68:285
  at async __wrapper (C:\igls\igls-next-firebase\node_modules\next\dist\lib\coalesced-function.js:1:330)
  at async DevServer.renderToHTMLWithComponents (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:91:254)
  at async DevServer.renderToHTML (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:92:254)
  at async DevServer.renderToHTML (C:\igls\igls-next-firebase\node_modules\next\dist\server\next-dev-server.js:22:539)
  at async DevServer.render (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:48:236)
  at async Object.fn (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:35:852)
  at async Router.execute (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\router.js:28:28)
  at async DevServer.run (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:44:494)
  at async DevServer.handleRequest (C:\igls\igls-next-firebase\node_modules\next\dist\next-server\server\next-server.js:13:133)

1.0.0 remark plugins stopped working

On 0.6.0 I had this

const mdx = await renderToString(
    content,
    components,
    {
      remarkPlugins: [
        remarkSlug,
        [
          remarkAutoLink,
          {
            content: {
              type: 'element',
              tagName: 'span',
              children: [{ type: 'text', value: '#' }],
              properties: { className: ['heading-link'] },
            },
          },
        ],
        remarkCodeTitles,
        remarkUnwrapImages,
      ],
    },
    data,
  );

And it worked perfectly to produce the following

image

And just by upgrading to 1.0.0 none of the remark plugins work with the same code. I get this in the source

image

See how no auto-link headings and ids exist. Same is the case with code, not being highlighted.

Question: Is it possible to use dynamic variables in the componet

I'm using a component that I'm rendering like in mdx
<CompA data={data}/>

Right now I need to write this line every time I want to render it.

Instead I'd like to do something like:
<CompA />
and it automatically will grab the data. For that in the component definition I've tried several approaches to pass default values
const CompA = ({data = '{data}') => { ...}

but this is not working and I'm not sure either if next-mdx-remote allows to parse this kind of components with dynamic variables.
Thanks

Importing 'next-mdx-remote/render-to-string' causes "Can't resolve 'fs'" Error

Including import renderToString from 'next-mdx-remote/render-to-string' in my file causes my Next app to crash with this error. The error kind of sprang up randomly. I deleted package.lock.json, node_modules, and .next and reinstalled everything, and am still receiving the error.

error - ./node_modules/@babel/core/lib/transformation/normalize-file.js:9:0
Module not found: Can't resolve 'fs'
null

The repo is located at https://github.com/agstover/kinetica and the page where the library is imported is here: https://github.com/agstover/kinetica/blob/master/pages/learn/%5B...trade%5D.js

Unable to make remark plugins work

Hi
I have the following remark plugins in my config. But I am unable to make them work. The only plugin that works is remark-prism plugin. Can you please tell me if I am doing something wrong here?

remarkPlugins: [
    require('remark-prism'),
    require('remark-autolink-headings'),
    require('remark-slug'),
    require('remark-code-titles'),
],

Among these, only remark-prism plugin works. I am unable to make the rest of these work.

Thanks in advance.

Additional Markdown in Components?

I'm trying to render additional Markdown/MDX within a Component:

markdown.mdx:

# Hello World

<Block>
```js
const x = 'hello world';
```
</Block>

index.jsx:

import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'
import Block from 'components/Block'

export default function Home({mdxSource}) {
   const content = hydrate(mdxSource, {
    components: {Block: Block}
   }

  return <div className="prose">{content}</div>
}

export async function getStaticProps({ params }) {
  const mdxSource = await renderToString(content, { components: { Block:Block }}
...
})

Block.jsx:

export default function Block({children}) {
  return <>{children}</>
}

Using renderToString in client side code

Hi
I wanted to know if there is a way to use renderToString in the client.

I have the following use-case.

  1. User starts writing a new blog post
  2. The MDX content of the blog post is stored in local storage
  3. Now, I want to show a preview of how this content will look like when rendered.

Since the only way to access local storage is in the client, is there a way to convert MDX to JSX in the client?

Thanks in advance.

Question: remark and rehype plugins seem not working

I'm trying to add the ability to type math in my mdx files, and I'm using the remark and rehype plugins like below:

 const mdxSource = await renderToString(content, {
      components,
      mdxOptions: {
        remarkPlugins: [remarkMath],
        rehypePlugins: [rehypeKatex]
      },
      scope: data
    });

but the parsed output does not contain parsed math equations, rather math equations are stilled wrapped around the p tags (<p> $$ ... $$ </p>).

What could be the problem?

Thank you.

Issues with using v3 Next Optimised images

Compatibility with Next Optimised images

The new version 3 (canary) of Next Optimised images using React Optimised Image from the same team.

The latter requires a babel plugin to work. Everything is great until you try to pass in options parameters in the url and then the babel throws an error on either server side (renderToString) or client side (hydrate) in which ever try to parse the img links.

Screenshot 2020-09-29 at 00 31 22

environment

version of node: 12.18.4
version of next-mdx-remote: 1.0.0
version of next-optimize-images: 3.0.0-canary.10
version of react-optimized-image: 0.4.1
version of next: 9.5.4-canary.22

Steps to reproduce

I am struggling to get it working on codesandbox as the same setup I have locally that is working doesn't work there ๐Ÿคท Let me know if you need anything in particular though.

Expected behaviour
adding ?lqip to the end of the url in the require for the images should result in low quality blurred image being served.

Actual behaviour
Babel throws an error trying to parse the mdx.

Export `renderToString()` and `hydrate()` as members of next-mdx-remote

Today, we have to declare two separate import statements to get the renderToString and hydrate functions like so:

import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'

It would be great if we could just import members from next-mdx-remote instead like this:

import { hydrate, renderToString } from 'next-mdx-remote'

Additionally, in package.json there is already a "main": "index.js" parameter declared, but no index.js file! So we could essentially just add an index.js file and export these members from that file and the above would just work.

With semver this should not be a breaking change since anyone who has the individual imports declared in their code should not need to make any changes as those files are not being moved anywhere.

This is a pretty low effort update to make with adding an index.js file and updating the docs a little. I can go ahead and make a PR for this today. If there is a good reason not to do this, feel free to decline the PR. It is not going to take a lot of time.

Imports in mdx files

Hi, I have a quick question. The documentation doesn't say whether it is possible to load MDX files, which can load any React components via relative module paths.

Specifically, I get an error message with the following statement when trying to do so. MDX files that do not have any import statements are loaded.

ReferenceError: require is not defined

On Stack-Overflow, a similar problem is shown.

https://stackoverflow.com/questions/63957018/how-to-use-images-in-a-mdx-file-outside-of-public-folder-while-using-next

A commentary on the mentioned contribution says

"It's not possible, unfortunately, as next-mdx-remote treats the markdown content as data & doesn't pass through Webpack at all.

There is a merge request (https://github.com/hashicorp/next-mdx-remote/pull/39/files), which among other things, changes the function of renderToString and executes a transformation through Babel.

Does this mean that your library will support this ability in the future, or is an MDX loader required that implements all content via module-based imports?

Thx!

Code blocks that contain import statements are picked up as attempts to literally import things

Hey! First off, thanks a bunch for this package! I was pretty excited to get it working :) The only issue is if I try to include code blocks that have import statements:

Support for the experimental syntax 'flow' isn't currently enabled (1:8):

I'm using prism-react-renderer, which supports import statements in code. It seems next-mdx-remote is reading those imports literally and expecting to import a package. My page rebuilds just fine if I get rid of the import statements.

Example (gibberish) code that triggers this:

a;

b;

It seems the newline is the issue, for whatever reason ๐Ÿค”

Edit: This seems relevant: mdx-js/mdx#218. I think it's an MDX/remark issue.

Flash of unstyled components when using MDXProvider.

This might be a duplicate of #32, actually.

When using an MDXProvider (placed in _app.tsx) to pass along custom components (for example an inlineCode component with custom class) the renderedOutput passed into the hydrate function is missing the custom class, but after the hydration step this custom MDXComponent gets rendered and the class is added.

This leads to flashes of un-styled components (In this case it's minimal, but imagine having custom H1 or other components affecting the margins of the page). It also kind-of affects SEO since the pre-rendered output of the page won't match the "hydrated" version.

I'm not sure if this is a limitation of the library or something that can be fixed. I'd be happy to work on a PR if the latter is the case.

Question: How to pass mdxcomponents to render the custom coponents.

Hey,

I started moving my blog from nextjs-mdx-enchanced to this library. I followed the steps, but for some reasons the page is not getting render properly. Aparently the componets theme is not getting applied. So on mdx-enhanced, I used to pass my MDXCompontents to MDXProvider and it used to wrap my blog's body. Now I am passing the mdxcoponent to the renderToString() and hydrate. Any help would be appreciated, I might be doing something wrong here if any one can help.

Prevent using unsafe evaluation

Using new Function(...) is an unsafe evaluation and is strictly violating the practises for Content Security Policy as mentioned here. This is a possible XSS attack vector.

This single line of code will raise a red flag for so many companies/people from using this package due to this security concern. I would request the contributors/maintainers to find an alternate way to accomplish the same and hopefully fix this in the upcoming releases with priority.

Apart from that, it's a really great project. ๐Ÿ‘

Question: Consuming an existing MDXProvider

Possibly related to #10 and #9

I've been following the next-mdx-remote example in Next.js repo and have managed to successfully migrate my next-mdx-enhanced implementation content and rehypePlugins, but I'm really struggling to get it working with my existing MDXProvider

I'm using Theme-UI's ThemeProvider, which means a lot of my MDX components are consuming from the Theme Provider (which is nested inside this provider). Should I be passing something into scope to ensure my components are referencing my MDXProvider (and its nested ThemePovider)?

Would really appreciate any support on this!

React v17.0.1 compatibility

Hi,

Simple one: is there any concern with React 17.0.1? If not is it possible to soften the <17.0.1 peerDependency rule?

Thanks a lot!

Question: passing an extra prop to the wrapper component

I'm using the function renderToString like this:

 const mdxSource = await renderToString(content, {
    components,
    mdxOptions: {
      remarkPlugins: [math],
    },
    scope: data
  });

components contains wrapper which allows me to style the layout (wrapper: ({ children, ...props }) => {//...}. Is it possible to pass a prop into my wrapper component so that I have access to it inside wrapper? I tried to follow the provider discussion

#35

but I'm not sure if that's what I'm looking for.

By the way, what is scope for and where is it accessible?

Thanks!

Question: how do I render index.mdx as / instead of /index?

First thanks for this awesome library, it checks all the boxes for creating documentation using Next.js and MDX.

I was wondering if there's anyway to render my /content/index.mdx as / instead of /index?

My setup is pretty much the same as the example/blog, but my [slug].js file lives inside pages/:

content/
โ”œโ”€โ”€ index.mdx
โ”œโ”€โ”€ another-page.mdx
pages/
โ”œโ”€โ”€ index.jsx
โ”œโ”€โ”€ [slug].jsx

I don't have the slight idea of where to look to change this.

Any help much appreciated, thanks!

How to migrate from next-mdx-enhanced to next-mdx-remote?

I see that the README includes drawbacks of next-mdx-enhanced. It'd be great if you can write something on how to move from there to next-mdx-remote, for new users.

Some implementations use next-mdx-enhanced in next-config.js. For example;

const readingTime = require('reading-time');
const mdxPrism = require('mdx-prism');
const withMdxEnhanced = require('next-mdx-enhanced');
const isProd = process.env.NODE_ENV === 'production'

module.exports = withMdxEnhanced({
  layoutPath: 'layouts',
  defaultLayout: true,
  remarkPlugins: [
    require('remark-autolink-headings'),
    require('remark-slug'),
    require('remark-code-titles'),
    require('./utils/title-style')
  ],
  rehypePlugins: [mdxPrism],
  extendFrontMatter: {
    process: (mdxContent) => ({
      wordCount: mdxContent.split(/\s+/gu).length,
      readingTime: readingTime(mdxContent)
    })
  }
})({
  webpack: (config, { isServer }) => {
    if (isServer) {
      require('./scripts/generate-sitemap');
    }

    return config;
  }
});

Chakra UI: `dangerouslySetInnerHTML` did not match.

When using components from Chakra UI i get this error:

react-dom.development.js?61bb:67 Warning: Prop `dangerouslySetInnerHTML` did not match. Server: "<div>Some **mdx** text, with a component <p class=\"chakra-text css-bk9fzy\">Test</p></div>" Client: "<div>Some **mdx** text, with a component <style data-emotion=\"css bk9fzy\">.css-bk9fzy{font-size:20px;}</style><p class=\"chakra-text css-bk9fzy\">Test</p></div>"
import { Text } from '@chakra-ui/react'
import hydrate from 'next-mdx-remote/hydrate'
import renderToString from 'next-mdx-remote/render-to-string'

const components = { Text }

export default function TestPage({ source }: any) {
  const content = hydrate(source, { components })
  return <div className="wrapper">{content}</div>
}

export async function getStaticProps() {
  const source = '<div>Some **mdx** text, with a component <Text fontSize={20}>Test</Text></div>'
  const mdxSource = await renderToString(source, { components })
  return { props: { source: mdxSource } }
}

For now the solution just remove the components from staticProps, like so and it works, bus is there a better solution?:

export async function getStaticProps() {
  const source = '<div>Some **mdx** text, with a component <Text fontSize={20}>Test</Text></div>'
  const mdxSource = await renderToString(source)
  return { props: { source: mdxSource } }
}

I get the following error from webpack

I get this error. I think it's linked to this issue floatdrop/require-from-string#18

Module not found: Can't resolve 'module' in '/var/app/node_modules/require-from-string'
ModuleNotFoundError: Module not found: Error: Can't resolve 'module' in '/var/app/node_modules/require-from-string'
    at /var/app/node_modules/webpack/lib/Compilation.js:925:10
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:401:22
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:130:21
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:224:22
    at /var/app/node_modules/neo-async/async.js:2830:7
    at /var/app/node_modules/neo-async/async.js:6877:13
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:214:25
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:213:14
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/var/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
    at /var/app/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js:44:7
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/var/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/var/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
    at /var/app/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:67:43
[ error ] ./node_modules/require-from-string/index.js
Module not found: Can't resolve 'module' in '/var/app/node_modules/require-from-string'

I have this in my next.config.js

module.exports = {
  webpack: (config, { isServer }) => {
    // Fixes npm packages that depend on `fs` module
    if (!isServer) {
      config.node = {
        fs: 'empty',
      };
    }

    return config;
  },
};

If I have an empty config I get this error:

[ error ] ./node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/core/lib/transformation/normalize-file.js
Module not found: Can't resolve 'fs' in '/var/app/node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/core/lib/transformation'
ModuleNotFoundError: Module not found: Error: Can't resolve 'fs' in '/var/app/node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/core/lib/transformation'
    at /var/app/node_modules/webpack/lib/Compilation.js:925:10
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:401:22
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:130:21
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:224:22
    at /var/app/node_modules/neo-async/async.js:2830:7
    at /var/app/node_modules/neo-async/async.js:6877:13
    at /var/app/node_modules/webpack/lib/NormalModuleFactory.js:214:25
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:213:14
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/var/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
    at /var/app/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js:44:7
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/var/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
    at /var/app/node_modules/enhanced-resolve/lib/Resolver.js:285:5
    at eval (eval at create (/var/app/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
    at /var/app/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:67:43

dependencies in package.json

  "dependencies": {
    "@apollo/react-hooks": "^3.1.4",
    "@apollo/react-ssr": "^3.1.4",
    "@mdx-js/runtime": "^1.6.1",
    "@types/react": "^16.8.0",
    "apollo-cache-inmemory": "^1.6.5",
    "apollo-client": "^2.6.8",
    "apollo-link-http": "^1.5.17",
    "apollo-link-ws": "^1.0.20",
    "apollo-utilities": "^1.3.3",
    "graphql": "^14.3.1",
    "graphql-tag": "^2.10.3",
    "isomorphic-unfetch": "^3.0.0",
    "mdx.macro": "^0.2.9",
    "next": "^9.3.4",
    "next-mdx-remote": "^0.3.0",
    "next-seo": "^4.4.0",
    "next-translate": "^0.14.3",
    "next-with-apollo": "^5.0.0",
    "nprogress": "^0.2.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "subscriptions-transport-ws": "^0.9.16"
  }

Exclude some components from hydration

Is it possible to exclude some components from re-render by the hydrate function?
In my example I have a custom Banner component that I need to SSR only without rehydrate it because this execution conflicts with the ad-server execution too.

I tried to not execute the hydrate completely and it works like expected but I need to "isolate" only one component type...

Thanks

ReferenceError: MDXContent is not defined

Hello, Thank you for you hard work :) great plugin !
I'm getting the following

Unhandled Runtime Error
ReferenceError: MDXContent is not defined

Call Stack
   eval
node_modules\next-mdx-remote\hydrate.js (46:11)

<unknown>
   /_next/static/chunks/pages/next.js (116720:8)

below is my code, the one in the example file

slide.jsx

import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'

export default function Slide({ source }) {
    const content = hydrate(source)
    return <div className="wrapper">{content}</div>
}

export async function getStaticProps() {
    // MDX text - can be from a local file, database, anywhere
    const source = 'Some **mdx** text, with a component'
    const mdxSource = await renderToString(source)
    return { props: { source: mdxSource } }
}

Page.jsx

...
const Next = () => {
     const classes = useStyles({});
     return (
      <React.Fragment>
       <Head>
        <title>Home - Nextron (with-javascript-material-ui)</title>
      </Head>
      <div className={classes.root}>
        <Slide source={"## testing"} />
      </div>
    </React.Fragment>
  );
};

export default Next;

Am i missing something?
I'm trying to load a local mdx file using fs and display it

Allowing for only hydrating some components

It would be nice to only hydrate some components, and have the others be the ones generated during the build step. My use case for this is that I have some components that don't use the client side features of React, but do import libraries for doing things. These libraries then get included in my bundle when they aren't needed on the client side and more rendering happens on the client side.

I expect this might be something that needs changes to MDX, but I'm not sure what so I thought I'd raise the issue here

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.