.agents/skills/canvas-data-fetching/SKILL.md
Fetch and render Drupal content in Canvas components with JSON:API and SWR patterns. Use when building content lists, integrating with SWR, or querying related entities. Covers JsonApiClient, DrupalJsonApiParams, relationship handling, and filter patterns.
npx skillsauth add balintbrews/canvas-starter canvas-data-fetchingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use SWR for all data fetching. It provides caching, revalidation, and a clean hook-based API.
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then((res) => res.json());
export default function Profile() {
const { data, error, isLoading } = useSWR(
'https://my-site.com/api/user',
fetcher,
);
if (error) return <div>Failed to load</div>;
if (isLoading) return <div>Loading...</div>;
return <div>Hello, {data.name}!</div>;
}
To fetch content from Drupal (e.g., articles, events, or other content types),
use the autoconfigured JsonApiClient from the drupal-canvas package combined
with DrupalJsonApiParams for query building.
Important: Do not fabricate JSON:API resource payloads in Workbench mocks. Components that fetch data should render their real loading, empty, or error states in Workbench unless the user explicitly asks for a static, non-fetching preview shape.
import { getNodePath, JsonApiClient } from 'drupal-canvas';
import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
import useSWR from 'swr';
const Articles = () => {
const client = new JsonApiClient();
const { data, error, isLoading } = useSWR(
[
'node--article',
{
queryString: new DrupalJsonApiParams()
.addSort('created', 'DESC')
.getQueryString(),
},
],
([type, options]) => client.getCollection(type, options),
);
if (error) return 'An error has occurred.';
if (isLoading) return 'Loading...';
return (
<ul>
{data.map((article) => (
<li key={article.id}>
<a href={getNodePath(article)}>{article.title}</a>
</li>
))}
</ul>
);
};
export default Articles;
addIncludeWhen you need related entities (e.g., images, taxonomy terms), use addInclude
to fetch them in a single request.
Avoid circular references in JSON:API responses. SWR uses deep equality checks to compare cached data, which fails with "too much recursion" errors when the response contains circular references.
Do not include self-referential fields. Fields that reference the same
entity type being queried (e.g., field_related_articles on an article query)
create circular references: Article A references Article B, which references
back to Article A. If you need related content of the same type, fetch it in a
separate query.
Use addFields to limit the response. Always specify only the fields you
need. This improves performance and helps avoid circular reference issues:
const params = new DrupalJsonApiParams();
params.addSort('created', 'DESC');
params.addInclude(['field_category', 'field_image']);
// Limit fields for each entity type
params.addFields('node--article', [
'title',
'created',
'field_category',
'field_image',
]);
params.addFields('taxonomy_term--categories', ['name']);
params.addFields('file--file', ['uri', 'url']);
When building a component that displays a list of content items (e.g., a news listing, event calendar, or resource library), follow this workflow:
Before any JSON:API discovery or content-type checks, verify local setup:
.env file exists in the project root..env exists, verify CANVAS_SITE_URL is set. Read
CANVAS_JSONAPI_PREFIX if present; otherwise, use jsonapi.{CANVAS_SITE_URL}/{CANVAS_JSONAPI_PREFIX}. Success
means HTTP 200..env values are missing), ask
the user whether they want to:
.env instructions:
CANVAS_SITE_URL=<their Drupal site URL>CANVAS_JSONAPI_PREFIX=jsonapi (optional; defaults to jsonapi) Then wait
for the user to confirm they updated .env, and test the request again.vite.config.*) to troubleshoot connectivity.
Connectivity issues must be resolved via correct .env values and Drupal
site availability, not build tooling changes.Examine the design to understand what data each list item needs:
Before writing code, verify that an appropriate content type exists in Drupal:
Check the JSON:API endpoint of your local Drupal site (configured via
CANVAS_SITE_URL and CANVAS_JSONAPI_PREFIX environment variables) to find
a content type that matches the required structure. Use a plain fetch
request for this check, after passing the Setup gate.
If a matching content type exists, use it and note which fields are available.
If no matching content type exists, stop and prompt the user to create one. Provide:
Create the content list component using JSON:API to fetch content. Only use fields that actually exist on the content type—do not assume fields exist without verifying.
If the list includes filters based on entity reference fields (e.g., filter by category, filter by author):
This ensures filters stay in sync with the actual content in Drupal and new options appear automatically without code changes.
development
Use when work must be verified in local Canvas Workbench, or when the user asks to run, open, or check a component in Workbench. Verifies that Canvas Workbench is available through the project's package runner, starts the local Workbench dev server, and keeps Workbench verification as part of the implementation workflow.
tools
Style Canvas components with Tailwind CSS 4 theme tokens and approved utility patterns. Use when (1) Creating a new component, (2) Adding colors or styling to components, (3) Working with Tailwind theme tokens, (4) Adding or updating design tokens in global.css, (5) Creating color/style props, (6) Any change where component props are added/removed/renamed/retyped and can affect rendered styles. Covers @theme variables, CVA variants, and cn() utility.
tools
Use utility components to render formatted text and media correctly. Use when (1) Rendering HTML text content from props, (2) Displaying images, (3) Working with formatted text or media. Covers FormattedText and Image utilities.
testing
Push validated Canvas component changes to Drupal Canvas and recover from common push failures. Use after component work is complete and validated. Handles dependency-related push failures that require retry.