hashicorp / next-mdx-remote

Load MDX content from anywhere
Mozilla Public License 2.0
2.71k stars 141 forks source link

Does not yet support Next.js Version 13 #307

Closed maximilianschmitt closed 1 year ago

maximilianschmitt commented 2 years ago

I've noticed that next-mdx-remote doesn't work with Next.js 13.

To reproduce the issue, I tried running the official example with-mdx-remote using Next.js 13 and it doesn't work. Trying to load the example post, I get the following error:

CleanShot 2022-10-26 at 18 41 15@2x

How to reproduce

In my own project, I'm getting a similar error relating to imports of components, so I assume the issue might be related to changes in Next.js' bundler or build system.

I don't get the error if I run the tests in this repo (hashicorp/next-mdx-remote) with Next.js 13. For me that's another hint that the error might be related to bundling, since in this repo the test doesn't import next-mdx-remote through node_modules (see https://github.com/hashicorp/next-mdx-remote/blob/f5b0e74529908efd78b981bae7121847ed751b58/__tests__/fixtures/basic/pages/index.jsx#L5-L6)

Compiled Output

Here is the compiled output of node_modules/next-mdx-remote/index.js:

import React, { useState, useEffect, useMemo } from 'react';
import { jsxRuntime } from './jsx-runtime.cjs';
import * as mdx from '@mdx-js/react';

if (typeof window !== 'undefined') {
  window.requestIdleCallback =
    window.requestIdleCallback ||
    function (cb) {
      var start = Date.now();
      return setTimeout(function () {
        cb({
          didTimeout: false,
          timeRemaining: function () {
            return Math.max(0, 50 - (Date.now() - start))
          },
        });
      }, 1)
    };

  window.cancelIdleCallback =
    window.cancelIdleCallback ||
    function (id) {
      clearTimeout(id);
    };
}

/**
 * Renders compiled source from next-mdx-remote/serialize.
 */
function MDXRemote({ compiledSource, frontmatter, scope, components = {}, lazy, }) {
    const [isReadyToRender, setIsReadyToRender] = useState(!lazy || typeof window === 'undefined');
    // if we're on the client side and `lazy` is set to true, we hydrate the
    // mdx content inside requestIdleCallback, allowing the page to get to
    // interactive quicker, but the mdx content to hydrate slower.
    useEffect(() => {
        if (lazy) {
            const handle = window.requestIdleCallback(() => {
                setIsReadyToRender(true);
            });
            return () => window.cancelIdleCallback(handle);
        }
    }, []);
    const Content = useMemo(() => {
        // if we're ready to render, we can assemble the component tree and let React do its thing
        // first we set up the scope which has to include the mdx custom
        // create element function as well as any components we're using
        const fullScope = Object.assign({ opts: { ...mdx, ...jsxRuntime } }, { frontmatter }, scope);
        const keys = Object.keys(fullScope);
        const values = Object.values(fullScope);
        // now we eval the source code using a function constructor
        // in order for this to work we need to have React, the mdx createElement,
        // and all our components in scope for the function, which is the case here
        // we pass the names (via keys) in as the function's args, and execute the
        // function with the actual values.
        const hydrateFn = Reflect.construct(Function, keys.concat(`${compiledSource}`));
        return hydrateFn.apply(hydrateFn, values).default;
    }, [scope, compiledSource]);
    if (!isReadyToRender) {
        // If we're not ready to render, return an empty div to preserve SSR'd markup
        return (React.createElement("div", { dangerouslySetInnerHTML: { __html: '' }, suppressHydrationWarning: true }));
    }
    // wrapping the content with MDXProvider will allow us to customize the standard
    // markdown components (such as "h1" or "a") with the "components" object
    const content = (React.createElement(mdx.MDXProvider, { components: components },
        React.createElement(Content, null)));
    // If lazy = true, we need to render a wrapping div to preserve the same markup structure that was SSR'd
    return lazy ? React.createElement("div", null, content) : content;
}

export { MDXRemote };
BRKalow commented 2 years ago

Thanks for the report. We haven't had the opportunity to test it out with v13 yet, but we'll take a look at this soon!

AdamLeBlanc commented 2 years ago

I'm just going to piggyback off of this issue rather than creating a new one (can if the author prefers).

There seems to be a separate issue when server/static rendering with Next 13.


export default async function Post(props: Props) {
  const source = await serialize("Some **mdx**");
  return (
    <div>
      <MDXRemote {...source} />
    </div>
  );
}

will result in the following error

Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
TypeError: Cannot read properties of null (reading 'useState')

However, wrapping the component to force it to client render like so

"use client";

import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";

export default function MDXRemoteWrapper(props: MDXRemoteProps) {
  return <MDXRemote {...props} />;
}

It seems to "solve" the issue.

I'm too tired to look into it anymore tonight but may poke around tomorrow if no one else is looking into it. But it seems like wrapping it like this is needed without some reorganization of the project, so the MDXRemote component gets loaded in with the "use client" directive.

jmikrut commented 2 years ago

Hey all,

In addition to the above, I'm seeing the following build errors while trying to import { serialize } from 'next-mdx-remote/serialize':

export 'serialize' (reexported as 'serialize') was not found in './dist/serialize.js' (module has no exports)

This only happens when we try and enable the runtime: 'experimental-edge' Next config property though.

Thought we'd keep all the Next 13 issues in the same place if possible so just tacking on here.

Frostbourn commented 2 years ago

Same issue here. TypeError: Cannot read properties of undefined (reading 'default')

BRKalow commented 2 years ago

If anyone is willing to make a codesandbox or minimal reproduction repo, that would be very helpful!

Frostbourn commented 2 years ago

Here it is: test-app

Thank you!

jescalan commented 2 years ago

Just a small note here - we are aware on the nextjs team that the app directory doesn't yet support mdx-remote. We have it on our roadmap to address this, but aren't quite there yet. We're still working on fixing critical bugs and stabilizing it right now, as it's still beta.

I'll drop updates here when we start working on this!

mikewheaton commented 1 year ago

For anyone else who is blocked by this, you can create a wrapper component (outside of the /app folder) like so:

"use client";

import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";

interface MDXContentProps {
  source: MDXRemoteProps;
}

export default function MDXContent({ source }: MDXContentProps) {
  return <MDXRemote {...source} />;
}

And then within the /app folder, in a page.tsx file:

// This is the wrapper component above.
import MDXContent from "components/MDXContent";
import { serialize } from "next-mdx-remote/serialize";
...
const source = await serialize(article.content);
...
<MDXContent source={source} />

The "use client" directive (see docs) makes this a client component so that it has access to local state. I haven't done any extensive testing here, but I believe the tradeoff is that next-mdx-remote will be included in the JS bundle sent to the client and that the work of translating will take place there, even if the app is otherwise statically rendered.

AlanMorel commented 1 year ago

I can't even get it to work using the workarounds pasted above.

When I try, I get Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead..

Does anybody already have a repo they're willing to share that works (Next 13 + app directory + MDX), even if its using a workaround?

rimzzlabs commented 1 year ago

For anyone else who is blocked by this, you can create a wrapper component (outside of the /app folder) like so:

"use client";

import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";

interface MDXContentProps {
  source: MDXRemoteProps;
}

export default function MDXContent({ source }: MDXContentProps) {
  return <MDXRemote {...source} />;
}

And then within the /app folder, in a page.tsx file:

// This is the wrapper component above.
import MDXContent from "components/MDXContent";
import { serialize } from "next-mdx-remote/serialize";
...
const source = await serialize(article.content);
...
<MDXContent source={source} />

The "use client" directive (see docs) makes this a client component so that it has access to local state. I haven't done any extensive testing here, but I believe the tradeoff is that next-mdx-remote will be included in the JS bundle sent to the client and that the work of translating will take place there, even if the app is otherwise statically rendered.

I still got the Error: Unsupported Server Component type: undefined

ekafyi commented 1 year ago

For anyone else who is blocked by this, you can create a wrapper component (outside of the /app folder) like so [...]

This worked for me (new appDir with SSG), thanks @mikewheaton !

willin commented 1 year ago

For anyone else who is blocked by this, you can create a wrapper component (outside of the /app folder) like so:

"use client";

import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";

interface MDXContentProps {
  source: MDXRemoteProps;
}

export default function MDXContent({ source }: MDXContentProps) {
  return <MDXRemote {...source} />;
}

And then within the /app folder, in a page.tsx file:

// This is the wrapper component above.
import MDXContent from "components/MDXContent";
import { serialize } from "next-mdx-remote/serialize";
...
const source = await serialize(article.content);
...
<MDXContent source={source} />

The "use client" directive (see docs) makes this a client component so that it has access to local state. I haven't done any extensive testing here, but I believe the tradeoff is that next-mdx-remote will be included in the JS bundle sent to the client and that the work of translating will take place there, even if the app is otherwise statically rendered.

For anyone else who is blocked by this, you can create a wrapper component (outside of the /app folder) like so:

"use client";

import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";

interface MDXContentProps {
  source: MDXRemoteProps;
}

export default function MDXContent({ source }: MDXContentProps) {
  return <MDXRemote {...source} />;
}

And then within the /app folder, in a page.tsx file:

// This is the wrapper component above.
import MDXContent from "components/MDXContent";
import { serialize } from "next-mdx-remote/serialize";
...
const source = await serialize(article.content);
...
<MDXContent source={source} />

The "use client" directive (see docs) makes this a client component so that it has access to local state. I haven't done any extensive testing here, but I believe the tradeoff is that next-mdx-remote will be included in the JS bundle sent to the client and that the work of translating will take place there, even if the app is otherwise statically rendered.

I still got the Error: Unsupported Server Component type: undefined

undefined useState too.

Tylopilus commented 1 year ago

I am not able to use MDXContent in a clientcomp. I am getting TypeError: _jsxDEV is not a function as an error.

impl code:

// Article.tsx
'use client';

import { MDXRemote, MDXRemoteProps } from 'next-mdx-remote';

export function Article({ content }: { content: MDXRemoteProps }) {
  return <MDXRemote {...content} />;
}
// page.tsx
import { serialize } from 'next-mdx-remote/serialize';
import { Article } from '../../../components/Article';
import { getPostBySlug } from '../../../utils/markdownHelper';
type PageProps = {
  params: { post: string };
  searchParams?: { [key: string]: string | string[] | undefined };
};
export default async function Page({ params }: PageProps) {
  const { content } = getPostBySlug(params.post);
  const mdx = await serialize(content);
  return (
    <div>
      <Article content={mdx} />
    </div>
  );
}

using node 18 w/ next@13.0.6 and next-mdx-remote@^4.2.0

typeofweb commented 1 year ago

I am not able to use MDXContent in a clientcomp. I am getting TypeError: _jsxDEV is not a function as an error.

It works if you force-downgrade @mdx-js to 2.1.5:

"resolutions": {
    "@mdx-js/mdx": "2.1.5",
    "@mdx-js/react": "2.1.5"
},
Tylopilus commented 1 year ago

Can confirm this works.

For the npm users, the key here is "overrides"

 "overrides": {
    "@mdx-js/mdx": "2.1.5",
    "@mdx-js/react": "2.1.5"
  }
j471n commented 1 year ago

It works if you force-downgrade @mdx-js to 2.1.5:

I can confirm that it works. But I have one question for you @mmiszy. Doing this will break anything in future if you are using Next.js v13? The point is what are the stakes if we use version 2.1.5.

typeofweb commented 1 year ago

@j471n sadly, I have no clue. Let's see if the maintainers reply.

moelzanaty3 commented 1 year ago

I am not able to use MDXContent in a clientcomp. I am getting TypeError: _jsxDEV is not a function as an error.

It works if you force-downgrade @mdx-js to 2.1.5:

"resolutions": {
  "@mdx-js/mdx": "2.1.5",
  "@mdx-js/react": "2.1.5"
},

thanks, @mmiszy for your temp solution, as maybe someone does not know about selective dependency resolutions and did this workaround, it might produce Certain edge cases that may not work properly since this is a fairly new feature. so please add a TODO: in your code base to upgrade in the future one maintainer adds a hotfix.

worth reading

imtsuki commented 1 year ago

There is a better temporary solution without the need to downgrade @mdx-js/* to 2.1.5. Just add development: false to the mdxOptions argument within the serialize() call:

 const mdxSource = await serialize(content, {
   mdxOptions: {
     remarkPlugins: [],
     rehypePlugins: [],
+    development: false,
   },
});

In mdx-js/mdx#2045, compiled MDX source will use jsxDEV for rendering in development mode, while in production mode it uses jsx and jsxs. jsxDEV should be imported from react/jsx-dev-runtime, but it is not exported by src/jsx-runtime.cjs. This causes the TypeError: _jsxDEV is not a function error.

imtsuki commented 1 year ago

I opened a PR (#323) to fix this issue. Before it gets merged, you can use the temporary workaround.

zomars commented 1 year ago

I don't know if this is related but after upgrading to Next 13.1 we started getting this error:

TypeError: _jsx is not a function
image

Note: We're not using the app directory anywhere yet.

EDIT:

Here's what the compiled source looks like ``` { source: { compiledSource: '/*@jsxRuntime automatic @jsxImportSource react*/\n' + 'const {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\n' + 'const {useMDXComponents: _provideComponents} = arguments[0];\n' + 'function _createMdxContent(props) {\n' + ' const _components = Object.assign({\n' + ' p: "p",\n' + ' h4: "h4"\n' + ' }, _provideComponents(), props.components);\n' + ' return _jsxs(_Fragment, {\n' + ' children: [_jsx(_components.p, {\n' + ' children: "Vital App is an app that can can help you combine your health peripherals with your calendar."\n' + ' }), "\\n", _jsx(_components.h4, {\n' + ' children: "Supported Actions:"\n' + ' }), "\\n", _jsx(_components.p, {\n' + ' children: "Sleep reschedule automation: Had a hard night? 🌕\\nAutomatically reschedule your whole day schedule based on your sleep parameters. (Setup your desired configuration on installed apps page.)"\n' + ' })]\n' + ' });\n' + '}\n' + 'function MDXContent(props = {}) {\n' + ' const {wrapper: MDXLayout} = Object.assign({}, _provideComponents(), props.components);\n' + ' return MDXLayout ? _jsx(MDXLayout, Object.assign({}, props, {\n' + ' children: _jsx(_createMdxContent, props)\n' + ' })) : _createMdxContent(props);\n' + '}\n' + 'return {\n' + ' default: MDXContent\n' + '};\n', frontmatter: {}, scope: {} } } ```
cseas commented 1 year ago

Upgrading to the latest version of next-mdx-remote should fix the TypeError: _jsxDEV is not a function error.

npm i next-mdx-remote@4.2.1
diegofrayo commented 1 year ago

I don't know if this is related but after upgrading to Next 13.1 we started getting this error:

TypeError: _jsx is not a function
image

Note: We're not using the app directory anywhere yet.

EDIT:

Here's what the compiled source looks like

I can say that I still get the same error

evowizz commented 1 year ago

I can say that I still get the same error

After updating to the version 4.2.1, make sure to revert the temporary solution shared by @imtsuki. Essentially, you'd end up doing this:

 const mdxSource = await serialize(content, {
   mdxOptions: {
     remarkPlugins: [],
     rehypePlugins: [],
-    development: false,
   },
});
diegofrayo commented 1 year ago

It works for me, thanks!

zomars commented 1 year ago

Not working for us. We're on 4.2.1 And we're not using neither development: falsenor locking the version to "@mdx-js/mdx": "2.1.5"

EDIT:

Also noting that our error is sightly different:

TypeError: _jsx is not a function

instead of

TypeError: _jsxDEV is not a function

Trying to understand why a development build is trying to use _jsx instead of _jsxDEV. This is only happening on development mode. Making a production build seems to be working fine.

mbifulco commented 1 year ago

I'm experiencing this same issue, and did some investigating locally. It seems as though the changes made in https://github.com/hashicorp/next-mdx-remote/pull/323 introduced a bug that surfaces when next-mdx-remote is used on Next.js sites in development mode, and not in production builds.

For a temporary fix, I pinned the version of next-mdx-remote I'm using to 4.2.0, and dev mode came right back to mr. #323 is a small change, but i don't know a ton about how react/jsx-runtime is meant to work in dev vs prod mode, so I haven't been able to come up with a PR that will fix this package. Would be happy to help if someone understands the issue better than i do!

e: to be clear, I changed my package.json to the following to fix this for now:

"next-mdx-remote": "4.2.0",

don-esteban commented 1 year ago

To give a current summary. It took me a while, and I was getting the error back after updates etc. This definitly works:

...
"next": "^13.0.7",
"next-mdx-remote": "^4.2.1",
...

Do not use this 4.2.0 workaround anymore. Otherwise, you get the error back. Credits to @evowizz for the hint.

KBeDevel commented 1 year ago

My package.json:

...
"next": "13.1.1",
"next-mdx-remote": "^4.2.1",
...

I got this working by using:

await serialize(content, {
  mdxOptions: {
    development: process.env.NODE_ENV !== 'production',
  },
})

Works under next dev and next start execution contexts.

YannHulot commented 1 year ago

I am getting the same issue currently with the following dependencies.

... "next": "13.1.1", "next-mdx-remote": "^4.2.1", ...

YannHulot commented 1 year ago

My package.json:

...
"next": "13.1.1",
"next-mdx-remote": "^4.2.1",
...

I got this working by using:

await serialize(content, {
  mdxOptions: {
    development: process.env.NODE_ENV !== 'production',
  },
})

Works under next dev and next start execution contexts.

Unfortunately It does not work for me.

YannHulot commented 1 year ago

I downgraded to

...
"next-mdx-remote": "^4.2.0",
...

and that fixed the issue.

ramblehead commented 1 year ago

It looks there is an entanglement with next-remote-watch - at least in my settings.

In package.json:

"scripts": {
  "dev": "next-remote-watch ../content/pages ./public/static/locales",
}

Running yarn dev (above script) produces TypeError: _jsx is not a function. However, running yarn next dev works as expected with the latest versions of nextjs and next-mdx-remote.

BRKalow commented 1 year ago

Thanks y'all, I'll take a look at some of the reported issues with 4.2.1. Note that the previous workaround should likely be removed, and you might need to do a full re-install of next-mdx-remote:

npm uninstall next-mdx-remote && npm install next-mdx-remote

(or whatever the equivalent would be for your package manager of choice)

c0ntradicti0n commented 1 year ago

Hm, after combining all workarounds it worked.

I got:

updated to version 4.2.1

      const serializedResult = await serialize(content, {
           mdxOptions: {
    development: process.env.NODE_ENV !== 'production',
  }})
    "@mdx-js/mdx": "2.2.1",
    "@mdx-js/react": "2.2.1",

(overrides had no effect).

So to remove the previous workaround was not successful.

don-esteban commented 1 year ago

This is a working configuration. Just did npm install on a fresh clone and npm run dev. It runs on Vercel too. No need for serialize/mdxOptions

// package.json
{
  ...
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.2.1",
    "@fortawesome/free-regular-svg-icons": "^6.2.1",
    "@fortawesome/free-solid-svg-icons": "^6.2.1",
    "@fortawesome/react-fontawesome": "^0.2.0",
    "next": "^13.0.7",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  },
  "devDependencies": {
    "eslint": "^8.29.0",
    "eslint-config-next": "^13.0.7",
    "glob": "^8.0.3",
    "gray-matter": "^4.0.3",
    "next-mdx-remote": "^4.2.1",
    "sass": "^1.55.0",
    "yamljs": "^0.3.0"
  }
}
// index.js
...
<MDXContent>{description}</MDXContent>
...
export async function getStaticProps(context) {
  ...
  description: data.description ? await serialize(data.description) : null
  ...
// MDX Content Component
import { MDXRemote } from 'next-mdx-remote'
import LinkOrA from './link-or-a'

const components = {
  a: (props) => <LinkOrA {...props} />
}

export default function MDXContent({ children }) {
  if (children) {
    return (
      <MDXRemote {...children} components={components} />
      )
  }
  return null
}
c0ntradicti0n commented 1 year ago

Using SSR, then it's not needed as it turned out. But if you use serialize on client side, then the mdxOptions is needed and have to be switched depending on dev and prod mode.

import Head from 'next/head'
import {useEffect, useState} from 'react'
import {MDXRemote} from 'next-mdx-remote'
import {serialize} from 'next-mdx-remote/serialize'
import {console} from "next/dist/compiled/@edge-runtime/primitives/console";

const components = {
    a: (props) => <a href={props.href}> {props.children} </a>
}

export async function getStaticProps(context) {

    return {
        props: {description: await serialize("[WIKIPEDIA](http://wikipedia.org)")}

    }
}

function MDXContent({children, description}) {

    const [x, sX] = useState(null)
    useEffect(() => {
        ;(async () => {
            if (children)
                sX(await serialize(children, {mdxOptions: {development: true}}))
        })()
    }, [children])

    if (description) {
        return (
            <MDXRemote {...description} components={components}/>
        )
    }
    if (x) {
        return (
            <MDXRemote {...x} components={components}/>
        )
    }
    return null
}

export default function Home({description}) {

    return (
        <>
            <Head>
                <title>Create Next App</title>
                <meta name="description" content="Generated by create next app"/>
                <meta name="viewport" content="width=device-width, initial-scale=1"/>
                <link rel="icon" href="/favicon.ico"/>
            </Head>
            <MDXContent>[WIKIPEDIA](http://wikipedia.org)</MDXContent>
            <MDXContent description={description}/>
        </>
    )
}
BRKalow commented 1 year ago

Hey y'all, we've now get experimental support for server components, and the app directory. Take a look at the release notes & documentation and give it a try!

pszafer commented 1 year ago

Hi, thanks for this release! With typing I got 2 errors while testing: with MDXRemote:

'MDXRemote' cannot be used as a JSX component.
  Its return type 'Promise<Element>' is not a valid JSX element.
    Type 'Promise<Element>' is missing the following properties from type 'Element': type, props, key

with compileMdx:

Argument of type '{ source: string; options: { mdxOptions: {}; }; }' is not assignable to parameter of type 'MDXRemoteProps'.
  Property 'compiledSource' is missing in type '{ source: string; options: { mdxOptions: {}; }; }' but required in type 'MDXRemoteSerializeResult<Record<string, unknown>, Record<string, string>>'.

Should I create new issue or is it some error on my side?

AlanMorel commented 1 year ago

I am getting the same exact issues @pszafer, not an error on your side

magoz commented 1 year ago

I ran into the same issue and solved it using {/* @ts-expect-error Server Component */}.

It's documented on the Next 13 beta docs.

josiahwiebe commented 1 year ago

I'm getting a different error when trying to use next-mdx-remote/rsc:

SyntaxError: Unexpected token u in JSON at position 0
    at JSON.parse (<anonymous>)
    at parseModel (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:40:15)
    at initializeModelChunk (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:400:18)
    at resolveModelChunk (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:366:5)
    at resolveModel (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:635:5)
    at processFullRow (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:729:9)
    at processBinaryChunk (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:789:5)
    at progress (/Users/josiah/Developer/jwie.be/node_modules/next/dist/compiled/react-server-dom-webpack/client.js:842:5)

Probably on my end but I can't seem to figure out what might be causing it. AllI changed was upgrading next-mdx-remote to 4.3.0 and updated the component to match the docs.

Adding the {/* @ts-expect-error Server Component */} fixed my TS error but not this one.

josiahwiebe commented 1 year ago

A slight update to the above ☝️ I was using a custom component to wrap <MDXRemote> that looked like this:

import { MDXRemote } from 'next-mdx-remote/rsc'
import MDXComponents from '@components/mdx-components'
import type { VFileCompatible } from 'vfile'
import type { SerializeOptions } from 'next-mdx-remote/dist/types'

export function MdxContent({ source, options }: { source: VFileCompatible; options?: SerializeOptions }) {
  // @ts-expect-error Server Component
  return <MDXRemote {...source} components={MDXComponents} {...options} />
}

When I replaced that component in all of my pages with the native <MDXRemote /> component, it worked in development, but when I try to build for production I still get the same Unexpected token u in JSON... error.

DonaldLouch commented 1 year ago

I followed the documentation along with adding the above suggestion of //@ts-expect-error Server Component solution as I'm using TypeScript; and I'm now getting this error:

Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

My code is:

// components/MDXComp

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

import { paragraph } from "./MarkDownComponents"

const components = { 
    p: paragraph
}

export function MDXContent(props:any) {
  return (
    // @ts-expect-error Server Component
     <MDXRemote {...props} components={{ ...components, ...(props.components || {}) }}
    />
  )
}
// components/MarkDownComponents

import { Text ... } from "@chakra-ui/react"

...

const paragraph = (props: any) => (
    <Text fontSize="1.1rem" lineHeight="1.4rem">{props.children}</Text>
)

...

export { paragraph ... }
// app/page.tsx

...

import { MDXContent } from "../../../components/MDXComp";

...

export default function PAGE(props: any) {
...

    return (
        <>
             ...             
             <MDXContent source={MARKDOWNSOURCE} />
             ...             
        </>
     )      
}

Update

After some further digging I have discovered that this issue maybe caused from needing to use the 'use client' tag for ChakraUI on the majority of my pages. Linked Discussion Post

yannickschuchmann commented 1 year ago

It looks there is an entanglement with next-remote-watch - at least in my settings.

Same for me. next dev works. next-remote-watch produces _jsx is not a function

@ramblehead Did you find a current workaround?

ramblehead commented 1 year ago

@ramblehead Did you find a current workaround?

@yannickschuchmann not really - just found a versions combination that seems to work for me: next-mdx-remote@4.2.1 and next-remote-watch@2.0.0

yannickschuchmann commented 1 year ago

Just because I have read it over this whole issue. To help each other out with "version combinations" it's important to give precise versions. So stick to the version in your lock file. ^4.2.1 is rather a version suggestion. I had it in the past that something like a 4.2.1did work but a 4.3.0 did NOT work.

For safe & clear communication it's often better to give exact versions, especially for combinations of packages because ^4.2.1 can mean anything upwards that version.

// package.json

"next-mdx-remote": "^4.3.0",
"next-remote-watch": "^2.0.0",
// vs.
"next-mdx-remote": "^4.2.1",
"next-remote-watch": "^2.0.0",

As an example: for both variations your package manager will install the exact same packages.

Hope it's not too dramatic, I just love some helpful clarity in version talking :)

@ramblehead ofc not meant personally, maybe you even meant "4.2.1 and upwards". :)

ramblehead commented 1 year ago

@yannickschuchmann you are correct - I meant precise versions not ^ (“compatible with version” - e. g. future minor/patch versions).

P. S. When having a choice I tend to use yarn 3 monorepos to enforce versions consistency, yarn upgrade-interactive to update packages individually across monorepo, and yarn why to manually check consistency. That, mostly, avoids problems with ^ and ~ version prefixes.

BRKalow commented 1 year ago

Hi y'all,

As discussed above, we should fully support next 13 as well as the /app directory, documented here). I'm going to close this out, if you have specific issues working with next-mdx-remote and next 13 going forward, please feel free to open additional issues with reproductions. Thanks for the discussion here!