Closed maximilianschmitt closed 1 year 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!
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.
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.
Same issue here.
TypeError: Cannot read properties of undefined (reading 'default')
If anyone is willing to make a codesandbox or minimal reproduction repo, that would be very helpful!
Here it is: test-app
Thank you!
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!
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 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?
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 apage.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
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 !
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 apage.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 apage.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.
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
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"
},
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"
}
It works if you force-downgrade
@mdx-js
to2.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
.
@j471n sadly, I have no clue. Let's see if the maintainers reply.
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
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.
I opened a PR (#323) to fix this issue. Before it gets merged, you can use the temporary workaround.
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
Note: We're not using the app
directory anywhere yet.
EDIT:
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
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
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
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,
},
});
It works for me, thanks!
Not working for us. We're on 4.2.1
And we're not using neither development: false
nor 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.
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",
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.
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.
I am getting the same issue currently with the following dependencies.
... "next": "13.1.1", "next-mdx-remote": "^4.2.1", ...
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
andnext start
execution contexts.
Unfortunately It does not work for me.
I downgraded to
...
"next-mdx-remote": "^4.2.0",
...
and that fixed the issue.
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.
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)
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.
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
}
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}/>
</>
)
}
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!
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?
I am getting the same exact issues @pszafer, not an error on your side
I ran into the same issue and solved it using {/* @ts-expect-error Server Component */}
.
It's documented on the Next 13 beta docs.
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.
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.
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} />
...
</>
)
}
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
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
4.3.0
2.0.0_next@13.0.2
@ramblehead Did you find a current workaround?
@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
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.1
did 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". :)
@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.
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!
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:
How to reproduce
yarn dev
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
throughnode_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
: