Skip to content

feat(website): cacheable prop on Page to cache CMS-only pages - #1652

Open
igoramf wants to merge 2 commits into
mainfrom
feat/website-page-cacheable-prop
Open

feat(website): cacheable prop on Page to cache CMS-only pages#1652
igoramf wants to merge 2 commits into
mainfrom
feat/website-page-cacheable-prop

Conversation

@igoramf

@igoramf igoramf commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Problem

CMS-only pages (e.g. a home built purely from website sections, no commerce loader) are never CDN-cached.

The runtime (deco/runtime/middleware.tsapplyPageCacheDecision) is opt-in: it only emits a public Cache-Control when a middleware set PAGE_CACHE_ALLOWED_KEY on the request bag. The only setter is apps/vtex/middleware.ts — and an app middleware is composed as a resolver middleware (blocks/appsUtil.ts), so it only runs when a block from that app resolves. A page with no VTEX loader never triggers it → isPageCacheAllowed=false → no public Cache-Control → never cached.

Fix

Add an opt-in cacheable?: boolean to the Page block. When enabled, the existing Page loader sets PAGE_CACHE_ALLOWED_KEY (same API the VTEX middleware uses), routing the page through the existing caching pipeline.

if (cacheable) ctx.bag?.set(PAGE_CACHE_ALLOWED_KEY, true);

Enable Allow CDN caching on a public, non-personalized page (e.g. the home) to make it cacheable.

Safe by construction

The opt-in only lets the page reach the decision; the runtime guards still apply and override to no-store:

  • foreign (non-framework) Set-Cookieno-store
  • vary.shouldCache === false (a loader with cache:"no-store" or null cache key) → no-store
  • a matcher/flag with cacheable !== true (e.g. a non-cacheable A/B test) → no-store
  • a Cache-Control already set by another block (e.g. VTEX no-store for a logged-in/segmented user) wins — the runtime only sets the public directive if (!headers.has("Cache-Control")).

So it never forces caching of a personalized response. Default (unset/false) is fully backwards compatible — no existing page changes behavior unless explicitly toggled.

Tests

  • website/pages/Page.cacheable.test.tsx (new): the real Page loader sets PAGE_CACHE_ALLOWED_KEY iff cacheable=true; off/omitted leaves the bag untouched.
  • deco/runtime/middleware.test.ts (existing, 22 passing): confirms key + clean ⇒ public, isPageCacheAllowed=false ⇒ headers untouched, and every guard ⇒ no-store.

Verified compatible with the versions FARM pins today (@deco/deco@1.202.0 exports the key; apps@0.158.9 already uses it in the VTEX middleware).

🤖 Generated with Claude Code


Summary by cubic

Adds a cacheable prop to the website Page to let CMS-only pages opt into CDN caching via the existing runtime pipeline. Public, non-personalized pages can now be cached; default stays off.

  • New Features

    • cacheable?: boolean on Page; when true, the loader sets PAGE_CACHE_ALLOWED_KEY.
    • Respects runtime guards: Set-Cookie, no-store loaders, non-cacheable flags/matchers, or existing Cache-Control force no-store.
    • Backwards compatible by default (off).
  • Refactors

    • Cleaned up tests by removing unnecessary async in the Page.cacheable test.

Written for commit e67634d. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Pages can now explicitly opt in to caching with a cacheable setting.
    • Cache-enabled pages are marked for page-cache handling automatically.
  • Tests

    • Added coverage for enabled, disabled, and unspecified caching behavior.

… cache

CMS-only pages (a home built purely from website sections, no commerce
loader) never get PAGE_CACHE_ALLOWED_KEY set, so the runtime never emits a
public Cache-Control and they stay uncached. Commerce apps (VTEX) set that key
from their middleware, but an app middleware is a resolver middleware and only
runs when a block from that app is resolved.

Add an opt-in `cacheable` boolean to the Page block. When enabled, the Page
loader sets PAGE_CACHE_ALLOWED_KEY (same API/mechanism as the VTEX middleware),
routing the page through the existing, guarded caching pipeline.

Safe by construction: the runtime still forces no-store on a foreign
Set-Cookie, vary.shouldCache=false, or a non-cacheable matcher/flag, and never
overrides a Cache-Control already set by another block. Default (unset/false)
is fully backwards compatible — no page changes behavior unless explicitly
toggled.

Verified: Page.cacheable.test.tsx (loader sets the key iff cacheable=true) plus
deco's runtime/middleware.test.ts (key ⇒ public, guards ⇒ no-store).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Tagging Options

Should a new tag be published when this PR is merged?

  • 👍 for Patch 0.160.1 update
  • 🎉 for Minor 0.161.0 update
  • 🚀 for Major 1.0.0 update

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 26d10701-4e2c-4a53-abdc-4c13a00ddd3f

📥 Commits

Reviewing files that changed from the base of the PR and between 97502eb and e67634d.

📒 Files selected for processing (1)
  • website/pages/Page.cacheable.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/pages/Page.cacheable.test.tsx

📝 Walkthrough

Walkthrough

The page loader accepts an optional cacheable property. When enabled, it sets PAGE_CACHE_ALLOWED_KEY in ctx.bag and excludes the property from forwarded page props. Tests cover enabled, disabled, and omitted values.

Changes

Page cache opt-in

Layer / File(s) Summary
Cache property and loader behavior
website/pages/Page.tsx, website/pages/Page.cacheable.test.tsx
Props includes optional cacheable. The loader sets PAGE_CACHE_ALLOWED_KEY when the value is true and removes cacheable from forwarded props. Tests cover enabled, disabled, and omitted values.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • deco-cx/apps#1597: Both changes use PAGE_CACHE_ALLOWED_KEY in ctx.bag for page caching.
  • deco-cx/apps#1649: Both changes set PAGE_CACHE_ALLOWED_KEY through loader behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the addition of the cacheable prop to the Page component.
Description check ✅ Passed The description clearly explains the problem, fix, safeguards, migration, and tests, but it omits the template's issue, Loom, and demonstration links.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/website-page-cacheable-prop

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@website/pages/Page.cacheable.test.tsx`:
- Line 14: Remove the unnecessary async modifier from the get mock function in
the cacheable page test, keeping its existing synchronous object return
unchanged so it satisfies Deno’s require-await lint rule.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f2768afd-499f-434f-95fe-6598d2559550

📥 Commits

Reviewing files that changed from the base of the PR and between 292aed0 and 97502eb.

📒 Files selected for processing (2)
  • website/pages/Page.cacheable.test.tsx
  • website/pages/Page.tsx

Comment thread website/pages/Page.cacheable.test.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Re-trigger cubic

…-await)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant