Security News
38% of CISOs Fear They’re Not Moving Fast Enough on AI
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
react-content-manager
Advanced tools
Package is now stable, it could be used on production.
React Content Manager is a package that allows you to build your app from manageable components, that can be edited by users for their needs.
npm install react-content-manager
or
yarn add react-content-manager
You need to install @ant-design/v5-patch-for-react-19
, more here: https://ant.design/docs/react/v5-for-19#compatibility-package
npm install --save-dev @ant-design/v5-patch-for-react-19
inside your next.config.js add or merge with existing one:
/** @type {import('next').NextConfig} */
const nextConfig = {
// .. your other options
webpack: (config) => {
const path = require("path");
config.resolve.alias = {
...config.resolve.alias,
"cm.config.ts": path.resolve(__dirname, "cm.config.ts"),
"cm.fetcher.ts": path.resolve(__dirname, "cm.fetcher.ts"),
"cm.persister.ts": path.resolve(__dirname, "cm.persister.ts"),
};
return config;
},
};
module.exports = nextConfig;
Inside your vite.config.ts add or merge with existing one:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"cm.config.ts": path.resolve(__dirname, "cm.config.ts"),
"cm.fetcher.ts": path.resolve(__dirname, "cm.fetcher.ts"),
"cm.persister.ts": path.resolve(__dirname, "cm.persister.ts"),
},
},
});
import { CmConfig } from "react-content-manager";
// optionally last parameter is language, default is 'en', we support 'pl' as well
const cmConfig = new CmConfig("en");
export default cmConfig;
import { CmHostHandler } from "react-content-manager";
const fetcher = async (componentId: string): Promise<any> => {
return {};
};
export default fetcher;
"use client";
import { CmHostHandler } from "react-content-manager";
const persister = async (configId: string, componentId: string, data: any) => {
return {};
};
export default persister;
You can find working example inside demo catalog
You need 4 files:
minimal example for components (live inside demo-next app)
Component.tsx
export type ComponentProps = {
text?: string;
};
const Component = (props: ComponentProps) => {
const { text } = props;
return (
<div className="bg-white p-4 rounded-xl">
<div className="text-black">{text}</div>
</div>
);
};
// remember to export default the component!
export default Component;
Form.tsx
"use client";
import { useState } from "react";
import { useCMConfig } from "react-content-manager/dist/esm/client/useCMConfig";
import { ComponentProps } from "./Component";
interface ComponentForm {
configId: string;
componentId: string;
}
const Form = (props: ComponentProps & ComponentForm) => {
/**
* // If you are using next.js you might want refresh page after saving changes
* const router = useRouter();
* // then pass the onSuccess as parameter of saveChange function
* const OnSuccess = () => {
* router.refresh();
* }
**/
const { saveChange, isSaving } = useCMConfig();
const [limit, setLimit] = useState(props.text);
return (
<>
<form
className="p-5 flex flex-col gap-4"
onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const text = formData.get("text") as string;
await saveChange(props.configId, props.componentId, {
text: text,
});
}}
>
<div>
<input
type="text"
name="text"
defaultValue={props.text}
className="bg-white p-2 rounded-lg border border-gray-300"
/>
</div>
<div>
<button
type="submit"
className="bg-blue-500 text-white p-2 rounded-lg"
>
Save
</button>
</div>
</form>
</>
);
};
export default Form;
ReadProps.tsx
import { ComponentProps } from "./Component";
const readProps = async (
serializedProps?: ComponentProps,
): Promise<ComponentProps> => {
return {
text: serializedProps?.text ?? "Default text, edit me!",
};
};
export default readProps;
WriteProps.tsx
import { ComponentProps } from "./Component";
export const writeProps = async (props: ComponentProps) => {
const data = {
text: props.text,
};
return data;
};
export default writeProps;
cm.config.ts
import { CmConfig } from "react-content-manager";
// optionally last parameter is language, default is 'en', we support 'pl' as well
const cmConfig = new CmConfig("en");
// register your components
cmConfig.getComponentGallery().registerComponent({
id: "text-block", // unique id of component, you will use it in your code
name: "Text Block", // name of component, it will be visible in component gallery
desc: "optionally you can add description for the user", // description of component, it will be visible in component gallery
public: true, // should be visible in component gallery for users
componentPath: () => import("@/app/components/text-block/Component"), // path to component that will be rendered
formPath: () => import("@/app/components/text-block/Form"), // path to component with form that will be use to edit component props
readProps: () => import("@/app/components/text-block/ReadProps"), // path to function that will deserialize component props from your persistence layer
writeProps: () => import("@/app/components/text-block/WriteProps"), // path to function that will serialize component props to your persistence layer
tags: ["content", "alert"], // tags that will be used to filter components in component gallery
});
export default cmConfig;
cm.persister.ts
Architecture Note: We are separating persister from configuration because of nature of React Server Component. We need to asure it will work on client side, because component edition need interactivity
"use client";
import { CmHostHandler } from "react-content-manager";
const persister = async (configId: string, componentId: string, data: any) => {
const host = CmHostHandler.getHost();
const response = await fetch(`${host}/api/store`, {
method: "POST",
body: JSON.stringify({
configId: configId,
componentId: componentId,
data: data,
}),
});
};
export default persister;
cm.fetcher.ts
Architecture Note: If you are using Next.js framework code below will run on the server, otherwise it will run on client side
import { CmHostHandler } from "react-content-manager";
const fetcher = async (componentId: string): Promise<any> => {
let host = CmHostHandler.getHost();
const data = await fetch(`${host}/api/store?configId=${componentId}`, {
cache: "no-cache",
});
const json = await data.json();
return json;
};
export default fetcher;
page.tsx
import { CMComponent, CMProvider } from "react-content-manager";
export default function Home() {
return (
<CMProvider mode={"edit"}>
<CMComponent
configId="main_top"
componentId={"text-block"}
mode={"edit"}
/>
</CMProvider>
);
}
Run your project and go to http://localhost:3000 to see component gallery and edit mode.
Library supports container query in tailwindcss. You can use it in your components.
You need plugin to enable it -> https://tailwindcss.com/docs/plugins#container-queries
With tailwindcss v4 container query will be included inside framework! 🥳
It's very easy to run demo locally.
cd demo-next
npm install
npm run dev
The demo is using local file to persist data. It's only for demo purposes. In real project you should use your own persistence layer.
You can use npm link to develop package locally.
npm install
in root directorynpm install
in demo-next
directorynpm link
in root directorynpm link react-content-manager
in demo-next
directorynpm run watch
in root directorynpm run dev
in demo-next
directorysome useful commands in npm link
to list all npm links
npm ls -g --depth=0 --link=true
to remove npm link globally
npm unlink -g react-content-manager
FAQs
enable editable components in your react app
The npm package react-content-manager receives a total of 0 weekly downloads. As such, react-content-manager popularity was classified as not popular.
We found that react-content-manager demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.
Security News
Company News
Socket is joining TC54 to help develop standards for software supply chain security, contributing to the evolution of SBOMs, CycloneDX, and Package URL specifications.