'React / Next - importing component from external library cause : Element type is invalid: expected a string or a class/function but got: undefined
I'm trying to create a React components library then integrated into a NextJs project. In the library I have a generic "CmsBlock" component that take a type and the data and generate a component (Paragraph or ZigZag). Here is the code :
src/Block.tsx
import React from "react";
import Paragraph from "./components/Paragraph/Paragraph";
import ZigZag from "./components/ZigZag/ZigZag";
const CMS_BLOCKS: {
[key: string]: any;
} = {
ZigZag: ZigZag,
Paragraph: Paragraph,
};
const CmsBlock = ({ type, data }: { type: string; data: unknown }) => {
const CurrentBlock = CMS_BLOCKS[type];
if (!CurrentBlock) {
throw new Error("Unknown block type");
}
return <CurrentBlock data={data} />;
};
export default CmsBlock;
src/components/Paragraph/Paragraph.tsx
import React, { FC } from "react";
import { CmsProps } from "../props";
export interface ParagraphData {
text: string;
}
const Paragraph: FC<CmsProps<ParagraphData>> = ({ data }) => {
return (
<section className="text-gray-600 dark:text-gray-300">
<p className="leading-relaxed">{data.text}</p>
</section>
);
};
Paragraph.defaultProps = {
data: {
text: `Lorem ipsum dolor sit amet. Et ullam quis et repellat nulla sit consequatur praesentium est voluptatem dolorem ut voluptate eveniet At consequatur veniam aut vero pariatur? Et consequatur repudiandae ut illum voluptas est unde dolore et enim blanditiis et asperiores perspiciatis et rerum eveniet ad molestiae pariatur. Aut nostrum itaque a assumenda consequatur ad numquam expedita ex rerum officia? Eum delectus incidunt quo incidunt nemo est itaque similique est tempora iure.`,
},
};
export default Paragraph;
And then my index.ts (input for rollup)
export { default as CmsBlock } from "./Block";
In my NextJS project, I use the CmsBlock like that :
import { CmsBlock } from "cms-components";
export default function Bloc(data: {
blocks: { id: string; type: string; data: unknown }[];
}) {
return (
<section className="container mx-auto my-4 space-y-10">
{data.blocks.map((block) => (
<CmsBlock data={block.data} type={block.type} key={block.id} />
))}
</section>
);
}
It loads well but I have that modal with error :
Unhandled Runtime Error
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of
Bloc.
It look like I messed up with a import/export but I can't handle to find it.
edit : Just discover that when I try a "next build", I have that error :
./src/pages/[[...slug]].tsx
Attempted import error: 'CmsBlock' is not exported from '@cleverconnect/cms-components' (imported as 'CmsBlock').
Solution 1:[1]
It's likely that block.data and/or block.type are undefined when you pass them to CmsBlock in the Bloc component. You can check that the data.blocks is not empty before mapping it.
Either approach bellow would do:
import { CmsBlock } from "cms-components";
export default function Bloc(data: {
blocks: { id: string; type: string; data: unknown }[];
}) {
return (
<section className="container mx-auto my-4 space-y-10">
{data.blocks && data.blocks.map((block) => (
<CmsBlock data={block.data} type={block.type} key={block.id} />
))}
</section>
);
}
or :
import { CmsBlock } from "cms-components";
export default function Bloc(data: {
blocks: { id: string; type: string; data: unknown }[];
}) {
return (
<section className="container mx-auto my-4 space-y-10">
{data.blocks.length ? data.blocks.map((block) => (
<CmsBlock data={block.data} type={block.type} key={block.id} />
)) : <> Some custom fallback message here: no data yet, sorry</>}
</section>
);
}
Solution 2:[2]
Disclaimer: this is not really a solution, but more a dirty workaround.
I have exactly the same problem. It seems as thought the imported component is only undefined when NextJs does the initial render. Not sure exactly how that happens, but you can do a check to ensure that the import is not undefined before moving on with the real rendering.
import { CmsBlock } from "cms-components";
export default function Bloc(data: {
blocks: { id: string; type: string; data: unknown }[];
}) {
return CmsBlock ? (
<section className="container mx-auto my-4 space-y-10">
{data.blocks.map((block) => (
<CmsBlock data={block.data} type={block.type} key={block.id} />
))}
</section>
) : null;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | exaucae |
| Solution 2 | gdostie |

