Skip to content

Commit

Permalink
feat: support React Server Components and Next app router
Browse files Browse the repository at this point in the history
  • Loading branch information
edodusi committed Oct 18, 2024
1 parent c4f58d8 commit ecaf457
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
node_modules
dist
dist-v2
lib/cypress/screenshots/
lib/cypress/videos
.next
.next
70 changes: 70 additions & 0 deletions lib/rsc/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { storyblokInit as sbInit } from "@storyblok/js";

import {
SbReactComponentsMap,
SbReactSDKOptions,
StoryblokClient,
} from "../types";

let storyblokApiInstance: StoryblokClient = null;
let componentsMap: SbReactComponentsMap = {};
let enableFallbackComponent: boolean = false;
let customFallbackComponent: React.ElementType = null;

export const useStoryblokApi = (): StoryblokClient => {
if (!storyblokApiInstance) {
console.error(
"You can't use getStoryblokApi if you're not loading apiPlugin."
);
}

return storyblokApiInstance;
};

export const setComponents = (newComponentsMap: SbReactComponentsMap) => {
componentsMap = newComponentsMap;
return componentsMap;
};

export const getComponent = (componentKey: string) => {
if (!componentsMap[componentKey]) {
console.error(`Component ${componentKey} doesn't exist.`);
return false;
}

return componentsMap[componentKey];
};

export const getEnableFallbackComponent = () => enableFallbackComponent;
export const getCustomFallbackComponent = () => customFallbackComponent;

export const storyblokInit = (
pluginOptions: SbReactSDKOptions = {}
): (() => StoryblokClient) => {
if (storyblokApiInstance) {
return () => storyblokApiInstance;
}

const { storyblokApi } = sbInit(pluginOptions);
storyblokApiInstance = storyblokApi;

componentsMap = pluginOptions.components;
enableFallbackComponent = pluginOptions.enableFallbackComponent;
customFallbackComponent = pluginOptions.customFallbackComponent;

return () => storyblokApi;
};

export { default as StoryblokComponent } from "./storyblok-component";
export {
storyblokEditable,
apiPlugin,
loadStoryblokBridge,
useStoryblokBridge,
registerStoryblokBridge,
renderRichText,
RichTextResolver,
RichTextSchema,
} from "@storyblok/js";

export * from "../types";
8 changes: 4 additions & 4 deletions lib/rsc/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as BridgeLoader } from '../bridge-loader';
export { default as StoryblokBridgeLoader } from '../bridge-loader';
export * from '../common';
export { default as StoryblokStory } from '../story';
export * from "./common";
export { default as StoryblokStory } from "./story";
export { default as BridgeLoader } from "../bridge-loader";
export { default as StoryblokBridgeLoader } from "../bridge-loader";
24 changes: 24 additions & 0 deletions lib/rsc/story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import React, { forwardRef } from "react";
import { useStoryblokState } from "../common/client";
import { ISbStoryData, StoryblokBridgeConfigV2 } from "../types";
import { StoryblokComponent } from "./common";

interface StoryblokStoryRSCProps {
story: ISbStoryData;
bridgeOptions: StoryblokBridgeConfigV2;
[key: string]: unknown;
}

const StoryblokStory = forwardRef<HTMLElement, StoryblokStoryRSCProps>(
({ story, bridgeOptions, ...restProps }, ref) => {
if (typeof story.content === "string") {
story.content = JSON.parse(story.content);
}
story = useStoryblokState(story, bridgeOptions);
return <StoryblokComponent ref={ref} blok={story.content} {...restProps} />;
}
);

export default StoryblokStory;
54 changes: 54 additions & 0 deletions lib/rsc/storyblok-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { forwardRef } from "react";
import {
getComponent,
getEnableFallbackComponent,
getCustomFallbackComponent,
} from "./index";
import type { SbBlokData } from "../types";

interface StoryblokComponentProps {
blok: SbBlokData;
[key: string]: unknown;
}

const StoryblokComponent = forwardRef<HTMLElement, StoryblokComponentProps>(
({ blok, ...restProps }, ref) => {
if (!blok) {
console.error(
"Please provide a 'blok' property to the StoryblokComponent"
);
return (
<div>Please provide a blok property to the StoryblokComponent</div>
);
}

const Component = getComponent(blok.component);

if (Component) {
return <Component ref={ref} blok={blok} {...restProps} />;
}

if (getEnableFallbackComponent()) {
const CustomFallbackComponent = getCustomFallbackComponent();

if (CustomFallbackComponent) {
return <CustomFallbackComponent blok={blok} {...restProps} />;
} else {
return (
<>
<p>
Component could not be found for blok{" "}
<strong>{blok.component}</strong>! Is it configured correctly?
</p>
</>
);
}
}

return <div></div>;
}
);

StoryblokComponent.displayName = "StoryblokComponent";

export default StoryblokComponent;
2 changes: 1 addition & 1 deletion playground-next13-live-editing/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type {
ISbStoriesParams,
StoryblokClient,
} from '@storyblok/react/rsc';
import { getStoryblokApi } from "@/lib/storyblok";
import {
getStoryblokApi,
StoryblokStory,
} from '@storyblok/react/rsc';

Expand Down
6 changes: 6 additions & 0 deletions playground-next13-live-editing/lib/storyblok.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { apiPlugin, storyblokInit } from "@storyblok/react/rsc";

export const getStoryblokApi = storyblokInit({
accessToken: "OurklwV5XsDJTIE1NJaD2wtt",
use: [apiPlugin],
});
2 changes: 1 addition & 1 deletion playground-next13-rsc/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type {
ISbStoriesParams,
StoryblokClient,
} from '@storyblok/react/rsc';
import { getStoryblokApi } from "@/lib/storyblok";
import {
getStoryblokApi,
StoryblokComponent,
} from '@storyblok/react/rsc';

Expand Down
6 changes: 6 additions & 0 deletions playground-next13-rsc/lib/storyblok.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { apiPlugin, storyblokInit } from "@storyblok/react/rsc";

export const getStoryblokApi = storyblokInit({
accessToken: "OurklwV5XsDJTIE1NJaD2wtt",
use: [apiPlugin],
});

0 comments on commit ecaf457

Please sign in to comment.