← ReploCONTENT HISTORY
WHAT CHANGED
Update to Replo
Snapshot Oct 30, 2026 · 00:05 UTC · version 2.0.4
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Replo and check future changes in Following.
Up to 3 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "REQUIRED before generating pages with Okendo review data. Contains mandatory data loading patterns — code generated without reading this skill will use incorrect architecture.",
"included_files": [],
"name": "okendo",
"skill_md_contents": "---\nname: okendo\ntitle: Okendo Reviews\nsummary: Render Okendo reviews and star ratings on a page.\ndescription: \"REQUIRED before generating pages with Okendo review data. Contains mandatory data loading patterns — code generated without reading this skill will use incorrect architecture.\"\n---\n\n# Okendo Integration\n\n## Data Loading Pattern\n\n**Always use the prefetch pattern by default.** The only exception is when data must be loaded dynamically in response to a client-side input or interaction.\n\nThe pattern has two layers:\n\n1. **Server component (page):** Uses `PrefetchedLoaders` to fetch data during SSR and seed the React Query cache via `HydrationBoundary`. This ensures the first paint includes the data with no loading state.\n2. **Client component:** Uses a loader component (e.g., `OkendoReviewsLoader`) with `useSuspenseQuery` under the hood. On initial render it reads the prefetched cache — instant. On subsequent client-side navigations or prop changes, it fetches fresh data automatically.\n\n### Discovering the Shopify Product ID\n\nOkendo loaders require a **numeric Shopify product ID** (e.g. `\"12345\"`), not a Shopify GID. To get this:\n\n1. Get the product's numeric ID from the Shopify admin (the number at the end of the product's admin URL), or ask a Replo session to look it up. Given a GID like `gid://shopify/Product/12345`, take the numeric portion.\n2. Extract the numeric portion: `const numericId = gid.split(\"/\").pop()`.\n3. Pass the numeric ID to Okendo loader components as `shopifyProductId`.\n\n### Example: Reviews Section with Prefetch\n\n**Client component** (`app/components/ProductReviews.tsx`):\n\n```tsx\n\"use client\";\n\nimport { DATA_LOADER_KEYS } from \"@replohq/sdk/loaders/loader-keys\";\nimport { OkendoReviewsLoader } from \"@replohq/sdk/loaders/okendo-reviews-loader\";\n\nexport function ProductReviews({ shopifyProductId }: { shopifyProductId: string }) {\n return (\n <OkendoReviewsLoader\n loaderKey={DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEWS}\n shopifyProductId={shopifyProductId}\n fallback={<div>Loading reviews…</div>}\n >\n {({ reviews }) => (\n <div>\n <h2>{reviews.length} Reviews</h2>\n {reviews.map((review) => (\n <div key={review.id} className=\"border-b py-4\">\n <div className=\"flex items-center gap-2\">\n <span>{\"★\".repeat(review.rating ?? 0)}</span>\n <span className=\"font-semibold\">{review.title}</span>\n </div>\n {review.reviewerName && (\n <p className=\"text-sm text-muted-foreground\">by {review.reviewerName}</p>\n )}\n <p className=\"mt-2\">{review.body}</p>\n </div>\n ))}\n </div>\n )}\n </OkendoReviewsLoader>\n );\n}\n```\n\n**Server component** (`app/products/[handle]/page.tsx`):\n\n```tsx\nimport { DATA_LOADER_KEYS } from \"@replohq/sdk/loaders/loader-keys\";\nimport { ProductReviews } from \"@/app/components/ProductReviews\";\nimport { PrefetchedLoaders } from \"@replohq/sdk/loaders/prefetch-loaders\";\n\nconst SHOPIFY_PRODUCT_ID = \"12345\";\n\nexport default function ProductPage() {\n return (\n <PrefetchedLoaders\n queries={[\n {\n loaderKey: DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEWS,\n args: { shopifyProductId: SHOPIFY_PRODUCT_ID },\n },\n ]}\n >\n <ProductReviews shopifyProductId={SHOPIFY_PRODUCT_ID} />\n </PrefetchedLoaders>\n );\n}\n```\n\n### Example: Star Rating Widget with Aggregate Data\n\n**Client component** (`app/components/StarRating.tsx`):\n\n```tsx\n\"use client\";\n\nimport { DATA_LOADER_KEYS } from \"@replohq/sdk/loaders/loader-keys\";\nimport { OkendoReviewAggregateLoader } from \"@replohq/sdk/loaders/okendo-review-aggregate-loader\";\n\nexport function StarRating({ shopifyProductId }: { shopifyProductId: string }) {\n return (\n <OkendoReviewAggregateLoader\n loaderKey={DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEW_AGGREGATE}\n shopifyProductId={shopifyProductId}\n fallback={<div>Loading rating…</div>}\n >\n {(aggregate) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-foreground\">\n {\"★\".repeat(Math.round(aggregate.reviewAverageValue))}\n {\"☆\".repeat(5 - Math.round(aggregate.reviewAverageValue))}\n </span>\n <span className=\"text-sm text-muted-foreground\">\n {aggregate.reviewAverageValue.toFixed(1)} ({aggregate.reviewCount} reviews)\n </span>\n </div>\n )}\n </OkendoReviewAggregateLoader>\n );\n}\n```\n\n**Server component** — prefetch alongside reviews or product data:\n\n```tsx\n<PrefetchedLoaders\n queries={[\n {\n loaderKey: DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEW_AGGREGATE,\n args: { shopifyProductId: SHOPIFY_PRODUCT_ID },\n },\n {\n loaderKey: DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEWS,\n args: { shopifyProductId: SHOPIFY_PRODUCT_ID },\n },\n ]}\n>\n <StarRating shopifyProductId={SHOPIFY_PRODUCT_ID} />\n <ProductReviews shopifyProductId={SHOPIFY_PRODUCT_ID} />\n</PrefetchedLoaders>\n```\n\n## Available Loaders\n\n| Loader Key | Component | Import Path | Input |\n|---|---|---|---|\n| `DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEWS` | `OkendoReviewsLoader` | `@replohq/sdk/loaders/okendo-reviews-loader` | `{ shopifyProductId: string }` |\n| `DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEW_AGGREGATE` | `OkendoReviewAggregateLoader` | `@replohq/sdk/loaders/okendo-review-aggregate-loader` | `{ shopifyProductId: string }` |\n\n## Key Rules\n\n- **Always prefer the prefetch pattern** (server component with `PrefetchedLoaders` wrapping a client loader component).\n- Use `DATA_LOADER_KEYS` constants from `@replohq/sdk/loaders/loader-keys` — never raw strings.\n- The `shopifyProductId` must be a **numeric** product ID, not a GID. Extract it from the GID if needed.\n- Both Okendo loaders use the public Storefront API — no API key is needed at render time (only the `okendoUserId` from the project's connection, resolved server-side).\n- Loader components use React Query hooks → they **must** live in `\"use client\"` files.\n- `PrefetchedLoaders` is a **server component** → import it directly from `@replohq/sdk/loaders/prefetch-loaders`.\n"
}SHA-256 of public snapshot: 6eea52a37b31f531883a252c2f6066d466b983b18b884b707303ab89a4595913