Dynamic page to fetcher component not working but work in the editor/preview mode

I have a component set up like so:
image

Then I pass it along a fetcher component like so
image

And it does work with the preview mode, with value taken from my preview slug.

However it does not work when I run the code locally or when deployed at all. I have a react project and my App.tsx looks pretty much copy-and-pasted from the “CatchAllPage” example.

Please help

Hello @future_design, can you set the dynamic value to be $ctx.params.postSlug?

Yes
image

This is all fine in the editor. It even loads the correct data

I’m receiving “postSlug” as a props and did register it via plasmic-init

PLASMIC.registerComponent(WordpressPosts, {
  name: 'WordpressPosts',
  displayName: 'Wordpress Posts',
  providesData: true,
  importPath: './src/features/wordpress/components',
  props: {
    children: 'slot',
    className: 'string',
    language: 'string',
    showUntranslatedPosts: 'boolean',
    postSlug: 'string',
  },
});

It however does not receive it when loading via PlasmicComponent.

It’s just simple with nothing else

Just to simplify the problem I’m just creating a new page with these settings:

And then I have a text which prints that line

But it still doesn’t work.

Right now my work around is to just declare that page using react-router myself but that takes too much time and kinda defeats the purpose of using low-code in the first place

So I managed to get it working. This seems rather hacky and will require the slug set on plasmic editor to has the same name with the slug set in the fetcher component.

I’d love to hear improvements or alternatives or if I’m doing something wrong here

/*
  template: /products/[category]/[product]
  actualPath: /products/electronics/iphone
  returns: { category: 'electronics', product: 'iphone' }
*/
function extractDynamicSegments(template: string, actualPath: string): { [key: string]: string } {
  const templateParts = template.split('/');
  const actualParts = actualPath.split('/');
  const result: { [key: string]: string } = {};

  if (templateParts.length !== actualParts.length) {
    throw new Error('Template and actual path structures do not match');
  }

  for (let i = 0; i < templateParts.length; i++) {
    const templatePart = templateParts[i];
    const actualPart = actualParts[i];

    if (templatePart.startsWith('[') && templatePart.endsWith(']')) {
      const key = templatePart.slice(1, -1); // Remove brackets
      result[key] = actualPart;
    } else if (templatePart !== actualPart) {
      throw new Error(`Mismatch at segment ${i}: expected "${templatePart}", got "${actualPart}"`);
    }
  }

  return result;
}

// We try loading the Plasmic page for the current route.
// If it doesn't exist, then return "Not found."
export function CatchAllPage() {
  const [loading, setLoading] = useState(true);
  const [pageData, setPageData] = useState<ComponentRenderData | null>(null);
  const [slugsProps, setSlugsProps] = useState<any>(null);

  useEffect(() => {
    async function load() {
      const pageData = await PLASMIC.maybeFetchComponentData(location.pathname);
      const pageComponent = pageData?.bundle.components.find((c) => c.isPage);
      if (pageComponent && pageComponent.path && location.pathname) {
        const slugs = extractDynamicSegments(pageComponent.path, location.pathname);
        setSlugsProps(slugs);
      }

      setPageData(pageData);
      setLoading(false);
    }
    load();
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }
  if (!pageData) {
    return <div>Not found</div>;
  }

  // The page will already be cached from the `load` call above.
  return (
    <PageParamsProvider params={slugsProps}>
      <PlasmicComponent component={location.pathname} />
    </PageParamsProvider>
  );
}

Hey @future_design,

For loader-react I believe your solution to be the best if you want to use the CatchallPage, there won’t be any issues with the slug names.

If you don’t mind listing the specific paths manually, you could try:

function App() {
  return (
    <PlasmicRootProvider loader={PLASMIC}>
      <Router>
        <Routes>
            <Route path="/echo/:test" element={<PageComponent name={"Homepage"} />} />
          <Route path="/plasmic-host" element={<PlasmicCanvasHost />}/>
        </Routes>
      </Router>
    </PlasmicRootProvider>
  );
}

function PageComponent(props: { name: string}) {
  return <PageParamsProvider params={useParams()} ><PlasmicComponent component={props.name}  /></PageParamsProvider>
}