Skip to content

Instantly share code, notes, and snippets.

@jferrettiboke
Last active September 7, 2021 11:38
Show Gist options
  • Save jferrettiboke/b01ed07c9953f8f4ab9917f4f9a4ebe7 to your computer and use it in GitHub Desktop.
Save jferrettiboke/b01ed07c9953f8f4ab9917f4f9a4ebe7 to your computer and use it in GitHub Desktop.
Map CMS modules to React.js components idea
// CMS
const pages = [
{
id: "1",
slug: "/",
modules: [
{ type: "hero", title: "Hero title", description: "Hero description" },
{ type: "image", title: "Image title", url: "https://..." },
],
},
];
// components/builder.jsx
import moduleList from "@/components/modules";
export default function Builder({ components }) {
return (
<>
{components.map((component, i) => {
if (!component) {
return null;
}
const { name, props } = component;
const Component = moduleList[name] || null;
if (Component) {
return <Component key={`${i}-component-${name}`} {...props} />;
}
return (
<div key={`${i}-component-${name}`} {...props}>
<p>{name} cannot be resolved</p>
</div>
);
})}
</>
);
}
// components/hero.jsx
export default function Hero({ title, description }) {
return (
<div className="hero">
<div className="hero-title">{title}<div>
<div className="hero-description">{description}<div>
</div>
);
}
// components/image.jsx
export default function Image({ title, src }) {
return <img src={src} alt={title} />
}
// components/modules.js
import dynamic from "next/dynamic";
const modules = {
HeroComponent: dynamic(() => import("./hero")),
ImageComponent: dynamic(() => import("./image")),
};
export default modules;
// pages/[[...slug]].jsx
import Head from "next/head";
import Builder from "@/components/builder";
export default function Page({ page }) {
return (
<>
<Head>
<title>{page.title}</title>
</Head>
<Builder components={page.components} />
</>
);
}
export async function getStaticProps({ locale, params: { slug } }) {
const currentPage = await getPage({
slug: slug ? `/${slug.join("/")}` : "/",
locale,
});
const page = getPageProps({ page: currentPage, locale });
return {
props: {
page,
},
notFound: !!!page,
};
}
function getPageProps({ page, locale }) {
return {
slug: page.slug[locale],
title: page.title[locale],
components: page.modules.map((pageModule) => mapModuleToProps(pageModule)),
};
}
function mapModuleToProps({ type, ...pageModule }) {
switch (type) {
case "hero":
return {
name: "HeroComponent",
props: {
title: pageModule.title ?? null,
description: pageModule.description ?? null,
},
};
case "image":
return {
name: "ImageComponent",
props: {
title: pageModule.title ?? null,
src: pageModule.url ?? null,
},
};
default:
return undefined;
}
}
@PrimozRome
Copy link

PrimozRome commented Sep 7, 2021

Thanks, all clear.

My second question goes why the JSX inside builder.jsx return function starts in curly brackets {}?

// components/builder.jsx
export default function Builder({ components }) {
  return (
    <>
      { /* code here */ }
    </>
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment