## Summary Make `Resource<Document>` the lazy document-resolution abstraction shared by Content Engine graphs, generated sites, and Builder previews. Expressions use one async computation interface: referenced resource documents resolve first, independent requests run concurrently, and unused resources stay unloaded. Ref #6029 Ref #6031 ## Implementation checklist - [x] Define one resource contract for synchronous or asynchronous document resolution. - [x] Resolve explicit roots lazily with memoization, bounded concurrency, cancellation, and structured errors. - [x] Preserve cycle, duplicate, and missing-resource diagnostics. - [x] Extract resource dependencies with the existing expression AST. - [x] Discover consumed resource roots from the current page tree and page metadata. - [x] Generate a lazy request graph for published page data, including independent requests. - [x] Resolve dependency documents before evaluating dependent request expressions. - [x] Integrate dependency-first loading with Builder preview caching and invalidation. - [x] Expose safe Resource bindings in the editor and reject self/indirect cycles. - [x] Adapt REST, GraphQL, Assets, date, and sitemap requests through the same Resource contract. - [x] Cover laziness, chains, shared dependencies, concurrency, invalidation, cancellation, failures, and cycles with fail/pass tests. - [x] Review documentation impact and document dependent Resources in the Data variables guide. - [x] Track collection-item and component-parameter-dependent resources as follow-up #6031. - [x] Regenerate and review affected fixtures. - [x] Resolve action-resource request dependencies when an action is invoked. - [x] Reproduce and fix cached form submissions and multi-root dependency-cycle hangs. - [x] Document uncached form actions and regenerate affected fixtures. - [x] Preserve single and multi-selected content until a cut successfully writes to the clipboard. - [x] Preserve boolean expression values in the search-exclusion checkbox. - [x] Cancel cut deletion if project data changes while preparing or writing the clipboard; cover content/property edits and project switches for single and multi-selection, and document the retry behavior. - [x] Validate pending cuts after the final await, immediately before deletion, and reproduce the queued-update race for single and multi-selection. ## Technical direction The persisted, revisioned `DocumentGraph` remains the Content Engine artifact. `Resource<Document>` is the execution abstraction for obtaining a document; its resolver evaluates only the dependency closure reachable from requested roots. Static and remote documents therefore differ by Resource implementation, not by expression value type. Generated pages always emit a `ResourceRequestGraph`. Each node owns its output name, dependencies, and request factory. Roots come from resource variables actually referenced by the current page's metadata, props, actions, or expression children. Unused and off-page resources remain unresolved. The loader still accepts legacy request maps for compatibility. Builder preview uses the same page-root discovery as published generation. Its existing cache remains the lifecycle boundary: cached dependency documents unlock dependent requests, while invalidation removes downstream requests until their inputs resolve again. The separate canvas frame recomputes its local plan from the synchronized cache so rendered expressions receive those documents. Action requests and their dependencies resolve when a form is submitted. Each submission sends a new action request, bypassing Webstudio's resource cache. Dependency requests still use their configured cache lifetime. ## Review simplifications - Consolidated page resource-root discovery in the SDK and reused it in Builder. - Scoped root and action discovery to the current page tree. - Removed the redundant immediate-resource helper; `Resource.resolve` already accepts `Document | Promise<Document>`. - Moved output names onto graph nodes and removed the duplicate graph-level map. - Removed unused planner output and corrected transitive cycle discovery to include only Resource data sources. - Regenerated fixtures so independent resources also use lazy graph execution. - Reused one abort-aware concurrency limiter for document sessions and Resource resolution. - Collapsed duplicate generator action scans and removed redundant generated-resource bookkeeping. - Unified legacy request maps and request graphs through one resolution path. - Shared the 20-request concurrency limit between SDK, Builder preview, and the server batch boundary. - Preserved dependency failure paths and original causes, including the first cancellation reason. - Removed invalid-request logging that could expose Resource headers or request bodies. - Made editor cycle detection alias-safe by comparing underlying Resource IDs. - Memoized page settings values to prevent repeated async validation renders. ## Verification Latest local verification for `bbca6395e3`: - Both queued-update regression cases failed before the fix and passed afterward. Validation now runs synchronously immediately before deletion, after the final await, in both cut paths. - Passed: 255 copy/paste and command tests, Builder typecheck, lint, package boundaries, formatting, and diff checks. - Reviewed documentation: the existing copy/paste safety description remains accurate. Fixture inputs and outputs are unaffected; no fixture regeneration was needed. Agent evaluations were not run because separate approval is required. - CI for the new commit is pending. Previous local verification for `ffbc75b152`: - Six regression cases failed before the fix and passed afterward: content edits, property edits, and project switches during a pending cut, with both single and multiple selections. - Passed: 253 copy/paste and command tests, Builder typecheck, lint, package boundaries, source formatting, and diff checks. - An unchanged content-block test file initially failed to import in the browser. Its isolated rerun and the full 253-test rerun passed without code changes. - The guard conservatively treats any project-data change as a reason to keep the clipboard copy without deleting content. It shows a retry message. Copy/paste documentation now describes this behavior. - Fixture inputs and outputs are unaffected; fixtures were not regenerated. Agent evaluations were not run because separate approval is required. CI for the new commit is pending. Previous local verification for `3eb580697c`: - Reproduced both regressions with failing tests before fixing them: premature deletion during cut and false search exclusion appearing checked. - Passed: 203 copy/paste and page-settings tests, Builder typecheck, lint, package boundaries, formatting, and diff checks. - Cut coverage includes keyboard and programmatic entry points, single and multiple selections, delayed successful writes, rejected writes, and disabled copying. - Reviewed documentation impact: these fixes restore existing behavior; no documentation update is needed. Only Builder UI and clipboard files changed; fixture inputs and outputs are unaffected, so fixtures were not regenerated for this commit. - CI status for this older commit was recorded as pending at handoff. Agent evaluations were not run; they require separate approval. Previous local verification for `858b348ad`, rebased onto `cf606ca4c`: - Regression tests failed before each fix and passed afterward: repeated submissions in both generated route templates, plus three multi-root/branching cycle cases. - Passed: 590 SDK tests, 1,129 CLI tests, 72 focused Builder tests, SDK/CLI/Builder typechecks, lint, package boundaries, and fixture link/sync/build. - Initial concurrent runs hit timeouts. The complete CLI suite passed with one worker; the focused Builder suites passed without file parallelism. - Documentation and generated fixture changes reviewed. Agent evaluations were not run; they require separate approval. - CI for `858b348ad` passed: Main (including all six E2E shards), Visual Regression, Lint PR, and Check submodules. Earlier verification (before this update): TDD fail/pass evidence includes resolver creation, AST dependency discovery, published graph reachability, dependency-gated request generation, Builder dependency-first planning, Resource editor cycle filtering, page-tree root scoping, off-page action isolation, transitive Resource-only cycle discovery, abort/timeout precedence, server batch limits, and the rebased SSG fixture consuming its lazy Resource. - `pnpm checks` passed after rebasing onto `ce84ddcf12`; it covered workspace tests, typechecks, lint, package boundaries, generated API/docs validation, and fixture link/sync/build. - Earlier focused checks passed: Builder typecheck, 12 page-settings/pages unit tests, lint, formatting, and `git diff --check`. - Local generated-resource E2E passed for HTTP, GraphQL, and current-date resources (3 tests). - The page-actions and Marketplace page-settings persistence E2Es both pass locally. - CI for `6b8b08a70` passed: Main workflow (including all six Builder E2E shards), Visual Regression, Lint PR, and Check submodules. - Agent evaluations were not run because they require separate explicit approval. ## Current behavior and limitations - A failed dependency blocks its dependents and reports the resource path and cause. - One published resolution call is the memoization boundary; Builder maps dependency readiness onto its versioned request-cache lifecycle. - Scoped collection-item and component-parameter resources remain in #6031. - Builder skips cyclic dependency paths without blocking independent resources.
8.9 KiB
description
| description |
|---|
| Data variables enable the definition and use of a value throughout the page. |
🔡 Data variables
{% hint style="info" %} Hint: CSS variables are different than Data variables. They enable the reuse of values in the Style Panel. {% endhint %}
Data variables are defined on any instance in the navigator such as Global Root, Body, or Heading. Variables can be found on the right panel in the settings tab.
Variable scope
Variables are available within the instance they are defined on and any of its children. They are not available in the parent.
If you define a variable on the Global Root, it will be available on all pages and can be used within Slots.
{% hint style="info" %} To access variables in the Page Settings, the variable must be defined on the Body of the page. {% endhint %}
Global data variables
Variables defined on the Global Root are accessible across all pages in your project. This is powerful for:
- Reusable contact information – Define your email, phone, or address once and use it everywhere
- Site-wide settings – Store company name, social links, or other global data
- CMS configuration – Set up API endpoints that all pages can access
- Slot compatibility – Global variables work within Slots, enabling dynamic content in reusable components
Setting Up Global Variables
- In the Navigator, select Global Root (above Body)
- In the Settings panel, add a new variable
- Choose the variable type (String, JSON, Resource, etc.)
- The variable is now accessible on any page
Use Cases
{% embed url="https://www.youtube.com/watch?v=jRzMROoKwsQ" %}
- Contact email: Define once, bind to all contact forms and footers
- Simple CMS: Use JSON variables on Global Root for site-wide data that displays in Slots
- API keys: Store Resource configurations globally for consistent data fetching
Variables
System
System variable is a unique variable in that it exists by default, while all other variables are added by the user.
System variable contains the following:
- Params – Key/value pairs that are defined in Dynamic Page URLs.
- Search – Key/value pairs of query parameters that may exist in the URL.
- Origin – The URL of the current site. It will display whatever the actual URL is, so in the builder, it will be the internal wstd.io domain, but on the published site, it will be the current origin, likely a custom domain.
- Pathname – The current page's path (e.g.,
/blog/my-post). Useful for conditional logic based on the current URL.
String
A String variable holds text data, which can be used for content like titles, descriptions, and labels.
Number
A Number variable stores numeric values, including integers and decimals. It's useful for calculations, counters, and any numeric data that components might need to display or use in logic.
Boolean
A Boolean variable represents a true or false value. It is ideal for toggling states, such as visibility.
JSON
A JSON variable allows for structured data in JSON format. It is ideal for creating simple data structures used with Collections to iterate over each item, such as creating a dynamic gallery.
Resource
A Resource variable gets its value from a fetch request, allowing data from a remote system to be used within Webstudio. For example, Resource can be used to interact with a REST API. While it can also be used to interact with a GraphQL API, it’s recommended to use the GraphQL Resource instead.
There are several fields available to configure the fetch request.
{% hint style="info" %} Shortcut: The URL field supports pasting in a cURL command. Doing so will automatically populate the various fields within the Resource. Many API docs will provide you with a cURL command, so look out for it to save time. {% endhint %}
{% hint style="info" %} Copy & Paste: Resources can be copied and pasted between projects. Select an instance with a Resource variable defined, copy it, and paste into another project – the Resource configuration will be included. {% endhint %}
- URL – Where the resource is located.
- Method – A request method. Refer to the third-party API docs to find the suitable method.
- Search Params - Key/value pairs used for providing additional parameters as part of the URL. Values can be bound to other variables.
- Cache Max Age allows you to define your own cache lifetime.
- Headers – Key/value pairs such
Content-Type application/json. Refer to Request Headers for more info.
{% include "../../.gitbook/includes/the-requests-including-any....md" %}
Response
Once the Resource fetches data, you'll use the Expression editor to bind data from the response to your website. See Binding for more information.
Dependent resources
Bind a Resource request field to another Resource variable when one request needs data from an earlier response. For example, bind an author ID from one Resource into the URL or search parameters of a second Resource that loads the author's posts.
Webstudio fetches the dependency first and evaluates the dependent request after the dependency data is available. A Resource cannot depend on itself or on another Resource that already depends on it. Collection item and component parameter variables are not available in Resource request fields.
When a Form uses a Resource action, Webstudio fetches that action's dependencies when the form is submitted, not when the page loads.
Form actions bypass Webstudio's resource cache so each submission sends a new request. Their dependencies still use the configured cache lifetime.
Caching
You may be wondering whether every visit to your blog results in an API call to your CMS or if the content is cached in Webstudio.
When you configure a Resource, the data is fetched using Cloudflare Workers, which has a built-in caching system.
By default, several factors determine what gets cached and for how long, but one of the primary factors is what the origin system instructs the requesting entity to cache. In other words, the headless CMS tells Cloudflare Workers what can be cached, if anything, and for how long.
If you set a custom Cache Max Age value, it will define your cache lifetime instead of relying on API response headers.
GraphQL
A GraphQL Resource variable gets its value from a GraphQL API, allowing data from a remote system to be used within Webstudio. While similar to Resource, it’s unique in that the available fields are specifically designed for interacting with GraphQL APIs.
There are several fields available to configure the fetch request.
- URL – Where the resource is located.
- Query – A GraphQL query.
- Variables – A JavaScript object containing variables that will be passed into the request. This is commonly used to pass in parameters in a URL within a Dynamic Page. For example
{ slug: system.params.slug }See System Variable for more info.
{% include "../../.gitbook/includes/the-requests-including-any....md" %}
System Resource
A System Resource variable gets its value from internal data.
Available system resources:
- Sitemap – Contains data about the static pages on the website, commonly used to build a custom sitemap that combines dynamic data with static data. Refer to the XML Node component for more info.
- Current Date – Returns the current date/time, useful for displaying "today's date" or calculating relative times. Can be formatted using the Time component.
- Assets – Queries project assets, including structured fields and file content from Markdown, MDX, and JSON files. See Content Engine for a complete article workflow.
Related
- Expression editor – Bind variables to components and create expressions
- CMS – Use Resources to fetch content from external systems
- Collection – Iterate over JSON or Resource data
- CSS variables – Define reusable style values (different from Data variables)
- Slot – Access global variables in reusable components