rudra-js

RudraComponent

import { RudraComponent } from '@rudra-js/react';

RudraComponent lets you render the block from generator.generate, with every title, price, image and link read from your catalog.

RudraComponent(props: RudraComponentProps) => JSX.Element | null

Reference

RudraComponent(props)

Parameters

  • props: The spec, your catalog, and settings for how the block looks.
    • spec: The ComponentSpec that generator.generate returned.
    • products: Your products, as a list or as anything keyed by SKU with get and has, such as a Map. Pass input.candidates from parseTrackingInput, or products you checked with productSchema.
    • bundlesoptional: The sets you sell together, so a bundle block has something to draw. bundleSchema says which list to pass.
    • registryoptional: The renderer for each kind of block. Defaults to the built-in renderers. Build your own with extendRegistry.
    • hrefForSkuoptional: Returns the link for a product's SKU. Defaults to /product/{sku}, with the SKU URL-encoded.
    • formatPriceoptional: Returns the price text for a product. Defaults to defaultFormatPrice with your locale.
    • formatBundlePriceoptional: Returns the price text for a set. The default formats the set's own price in its own currency, the way defaultFormatPrice formats a product.
    • localeoptional: The language tag prices are formatted for, such as en-GB. Defaults to the locale of the machine that renders the block. On a server, that's rarely the shopper's, so pass it. Your own formatPrice and formatBundlePrice don't get it.
    • hasDiagnosticsoptional: Adds data-rudra-provider, data-rudra-model, data-rudra-latency-ms and data-rudra-degraded to the wrapper, and the spec's rationale at the end. Defaults to false. Keep it off on pages shoppers see. It tells a visitor which model you use and when it's failing.
    • classNameoptional: A class to add to the wrapper, next to its rudra class.

Returns

The block, as a <section> with the class rudra. It returns null when nothing is left to show.

Caveats

  • It uses no hooks, so it works on the server and in the browser. Render it in a Server Component or with renderToString, and the block is in the first HTML response. It needs no JavaScript in the browser.
  • A product that's missing from products, or marked isInStock: false there, doesn't show. Its card is left out. A hero that names it keeps its words and loses its link.
  • A block with nothing left to show is dropped. That's a grid or carousel with none of its products left, or a hero with no headline, no body and no product. A bundle is dropped when its set isn't in bundles, or when any product in the set doesn't show.
  • A products value that is neither a list nor keyed by SKU throws a TypeError that names the prop. A Set, a plain object and a Map that went through JSON all throw.
  • Everything the model wrote renders as escaped text.
  • It ships no CSS. Every element it renders has a class for you to style, listed in the React README.
  • Each product link carries data-rudra-sku, so you can track clicks. The React README lists every attribute.

Usage

Rendering the block on a Next.js product page

Put this in app/product/[sku]/page.tsx. It builds a block for the product in the URL and renders it on the server.

import { createComponentGenerator, parseTrackingInput } from '@rudra-js/core';
import { RudraComponent } from '@rudra-js/react';
import { cookies } from 'next/headers';

const generator = createComponentGenerator();

const catalog = [
  { sku: 'TR-102', title: 'Switchback Trail Shoe GTX', category: 'Trail Running', price: 174 },
  { sku: 'TR-104', title: 'Ridgeline Trail Shoe', category: 'Trail Running', price: 139 },
  { sku: 'SK-201', title: 'Merino Hiking Socks', category: 'Socks', price: 22 },
];

export default async function ProductPage({ params }: { params: Promise<{ sku: string }> }) {
  const { sku } = await params;
  const shopperId = (await cookies()).get('shopper')?.value ?? 'guest';

  const input = parseTrackingInput({
    user: { id: shopperId },
    context: { surface: 'pdp', currentSku: sku },
    candidates: catalog,
  });
  const spec = await generator.generate(input);

  return (
    <main>
      <RudraComponent spec={spec} products={input.candidates} locale="en-US" />
    </main>
  );
}

The generator is created outside the page, so every request shares it.

With no provider, you get the block core builds without a model. Turn the model on and this page stays the same.

Rendering with renderToString

When your pages come from something other than React, such as an Express route or a template engine, turn the block into an HTML string.

import { createComponentGenerator, parseTrackingInput, type Product } from '@rudra-js/core';
import { RudraComponent } from '@rudra-js/react';
import { renderToString } from 'react-dom/server';

const generator = createComponentGenerator();

export async function recommendationsHtml(shopperId: string, sku: string, catalog: Product[]) {
  const input = parseTrackingInput({
    user: { id: shopperId },
    context: { surface: 'pdp', currentSku: sku },
    candidates: catalog,
  });
  const spec = await generator.generate(input);

  return renderToString(<RudraComponent spec={spec} products={input.candidates} locale="en-US" />);
}

Put the string in your page where the block goes. It is empty when there was nothing to show.