diff --git a/apps/demo/app/[...puckPath]/client.tsx b/apps/demo/app/[...puckPath]/client.tsx
index 3d6bee1ce1..f9b7e643de 100644
--- a/apps/demo/app/[...puckPath]/client.tsx
+++ b/apps/demo/app/[...puckPath]/client.tsx
@@ -5,7 +5,7 @@ import { Puck } from "@/core/components/Puck";
import { Render } from "@/core/components/Render";
import { Button } from "@/core/components/Button";
import headingAnalyzer from "@/plugin-heading-analyzer/src/HeadingAnalyzer";
-import config from "../../config";
+import config, { UserConfig } from "../../config";
import { useDemoData } from "../../lib/use-demo-data";
export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
@@ -17,7 +17,7 @@ export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
if (isEdit) {
return (
-
config={config}
data={data}
onPublish={async (data: Data) => {
@@ -42,7 +42,7 @@ export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
}
if (data) {
- return ;
+ return config={config} data={resolvedData} />;
}
return (
diff --git a/apps/demo/app/custom-ui/[...puckPath]/client.tsx b/apps/demo/app/custom-ui/[...puckPath]/client.tsx
index 940d284212..957702a1ce 100644
--- a/apps/demo/app/custom-ui/[...puckPath]/client.tsx
+++ b/apps/demo/app/custom-ui/[...puckPath]/client.tsx
@@ -5,7 +5,7 @@ import { Puck } from "@/core/components/Puck";
import { Render } from "@/core/components/Render";
import { Button } from "@/core/components/Button";
import { HeadingAnalyzer } from "@/plugin-heading-analyzer/src/HeadingAnalyzer";
-import config from "../../../config";
+import config, { UserConfig } from "../../../config";
import { useDemoData } from "../../../lib/use-demo-data";
import { IconButton, usePuck } from "@/core";
import { ReactNode, useEffect, useRef, useState } from "react";
@@ -287,7 +287,7 @@ export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
if (isEdit) {
return (
-
config={config}
data={data}
headerPath={path}
@@ -336,7 +336,7 @@ export function Client({ path, isEdit }: { path: string; isEdit: boolean }) {
}
if (data) {
- return ;
+ return config={config} data={resolvedData} />;
}
return (
diff --git a/apps/demo/config/index.tsx b/apps/demo/config/index.tsx
index 37f974ad1d..1430e93ee0 100644
--- a/apps/demo/config/index.tsx
+++ b/apps/demo/config/index.tsx
@@ -27,12 +27,14 @@ export type Props = {
VerticalSpace: VerticalSpaceProps;
};
-// We avoid the name config as next gets confused
-export const conf: Config<
+export type UserConfig = Config<
Props,
RootProps,
"layout" | "typography" | "interactive"
-> = {
+>;
+
+// We avoid the name config as next gets confused
+export const conf: UserConfig = {
root: {
render: Root,
},
diff --git a/packages/core/components/DropZone/context.tsx b/packages/core/components/DropZone/context.tsx
index d6fc1a6df3..3bbeb2b396 100644
--- a/packages/core/components/DropZone/context.tsx
+++ b/packages/core/components/DropZone/context.tsx
@@ -15,9 +15,11 @@ import { getZoneId } from "../../lib/get-zone-id";
export type PathData = Record;
-export type DropZoneContext = {
+export type DropZoneContext<
+ UserConfig extends Config = Config
+> = {
data: Data;
- config: Config;
+ config: UserConfig;
componentState?: Record;
itemSelector?: ItemSelector | null;
setItemSelector?: (newIndex: ItemSelector | null) => void;
diff --git a/packages/core/components/Puck/context.tsx b/packages/core/components/Puck/context.tsx
index 61c5379741..bbe50a61f9 100644
--- a/packages/core/components/Puck/context.tsx
+++ b/packages/core/components/Puck/context.tsx
@@ -18,10 +18,12 @@ export const defaultAppState: AppState = {
},
};
-type AppContext = {
+type AppContext<
+ UserConfig extends Config = Config
+> = {
state: AppState;
dispatch: (action: PuckAction) => void;
- config: Config;
+ config: UserConfig;
componentState: Record;
resolveData: (newAppState: AppState) => void;
plugins: Plugin[];
@@ -42,8 +44,10 @@ export const appContext = createContext({
export const AppProvider = appContext.Provider;
-export const useAppContext = () => {
- const mainContext = useContext(appContext);
+export function useAppContext<
+ UserConfig extends Config = Config
+>() {
+ const mainContext = useContext(appContext) as AppContext;
const selectedItem = mainContext.state.ui.itemSelector
? getItem(mainContext.state.ui.itemSelector, mainContext.state.data)
@@ -61,4 +65,4 @@ export const useAppContext = () => {
});
},
};
-};
+}
diff --git a/packages/core/components/Puck/index.tsx b/packages/core/components/Puck/index.tsx
index 30d94ea8dc..b889c0e6b2 100644
--- a/packages/core/components/Puck/index.tsx
+++ b/packages/core/components/Puck/index.tsx
@@ -45,7 +45,9 @@ import { useHistoryStore } from "../../lib/use-history-store";
const getClassName = getClassNameFactory("Puck", styles);
-export function Puck({
+export function Puck<
+ UserConfig extends Config = Config
+>({
children,
config,
data: initialData = { content: [], root: { props: { title: "" } } },
@@ -60,7 +62,7 @@ export function Puck({
headerPath,
}: {
children?: ReactNode;
- config: Config;
+ config: UserConfig;
data: Data;
ui?: Partial;
onChange?: (data: Data) => void;
@@ -82,7 +84,7 @@ export function Puck({
const historyStore = useHistoryStore();
const [reducer] = useState(() =>
- createReducer({ config, record: historyStore.record })
+ createReducer({ config, record: historyStore.record })
);
const [initialAppState] = useState(() => ({
diff --git a/packages/core/components/Render/index.tsx b/packages/core/components/Render/index.tsx
index f8d391a5ef..69da3471bd 100644
--- a/packages/core/components/Render/index.tsx
+++ b/packages/core/components/Render/index.tsx
@@ -4,13 +4,9 @@ import { rootDroppableId } from "../../lib/root-droppable-id";
import { Config, Data } from "../../types/Config";
import { DropZone, DropZoneProvider } from "../DropZone";
-export function Render({
- config,
- data,
-}: {
- config: Config;
- data: Data;
-}) {
+export function Render<
+ UserConfig extends Config = Config
+>({ config, data }: { config: UserConfig; data: Data }) {
// DEPRECATED
const rootProps = data.root.props || data.root;
const title = rootProps?.title || "";
diff --git a/packages/core/components/ServerRender/index.tsx b/packages/core/components/ServerRender/index.tsx
index 0cbee99cd0..ec017fd7e7 100644
--- a/packages/core/components/ServerRender/index.tsx
+++ b/packages/core/components/ServerRender/index.tsx
@@ -59,13 +59,9 @@ function DropZoneRender({
);
}
-export function Render({
- config,
- data,
-}: {
- config: Config;
- data: Data;
-}) {
+export function Render<
+ UserConfig extends Config = Config
+>({ config, data }: { config: UserConfig; data: Data }) {
if (config.root?.render) {
// DEPRECATED
const rootProps = data.root.props || data.root;
diff --git a/packages/core/reducer/index.ts b/packages/core/reducer/index.ts
index 1d762d7c08..1508cf6f6f 100644
--- a/packages/core/reducer/index.ts
+++ b/packages/core/reducer/index.ts
@@ -49,14 +49,16 @@ export const setAction = (state: AppState, action: SetAction) => {
return { ...state, ...action.state(state) };
};
-export const createReducer = ({
+export function createReducer<
+ UserConfig extends Config = Config
+>({
config,
record,
}: {
- config: Config;
+ config: UserConfig;
record?: (appState: AppState) => void;
-}): StateReducer =>
- storeInterceptor((state, action) => {
+}): StateReducer {
+ return storeInterceptor((state, action) => {
const data = reduceData(state.data, action, config);
const ui = reduceUi(state.ui, action);
@@ -66,3 +68,4 @@ export const createReducer = ({
return { data, ui };
}, record);
+}
diff --git a/packages/core/types/Config.tsx b/packages/core/types/Config.tsx
index 4aabb95c0e..ffaf879924 100644
--- a/packages/core/types/Config.tsx
+++ b/packages/core/types/Config.tsx
@@ -173,7 +173,7 @@ type Category = {
export type Config<
Props extends { [key: string]: any } = { [key: string]: any },
RootProps extends DefaultRootProps = DefaultRootProps,
- CategoryName extends string = string
+ CategoryName extends string = any
> = {
categories?: Record> & {
other?: Category;