Loading...
Loading...
Build Shopify Hydrogen storefronts with Weaverse — components, schemas, loaders, theming, data fetching, React Router v7, deployment, and advanced features.
npx skill4agent add weaverse/shopify-hydrogen-skills weaverse-hydrogenBuild Shopify Hydrogen storefronts with Weaverse visual page builder. Docs: https://docs.weaverse.io | GitHub: https://github.com/Weaverse
node scripts/search_weaverse_docs.mjs "<query>"node scripts/get_weaverse_page.mjs "<page-path>"node scripts/search_weaverse_docs.mjs "component schema"
node scripts/search_weaverse_docs.mjs "data fetching"
node scripts/get_weaverse_page.mjs "development-guide/component-schema"
node scripts/get_weaverse_page.mjs "api-reference/weaverse-client"app/
├── components/ # Reusable UI components
├── graphql/ # GraphQL queries & fragments
├── hooks/ # Custom React hooks
├── routes/ # React Router v7 route files
├── sections/ # Weaverse section components ← YOUR WORK GOES HERE
├── styles/ # Global styles + Tailwind
├── weaverse/
│ ├── components.ts # Component registry
│ ├── schema.server.ts # Theme schema (global settings)
│ └── csp.ts # Content Security Policy for Weaverse
├── entry.client.tsx
├── entry.server.tsx
└── root.tsx # Wrapped with withWeaverse(App)
server.ts # WeaverseClient initialization
vite.config.ts
react-router.config.ts
tailwind.config.js
.env// app/sections/my-section/index.tsx
// 1. Default export — React component
function MySection(props: MySectionProps) { ... }
export default MySection;
// 2. Schema export — editor configuration
export let schema = createSchema({ ... });
// 3. Loader export (optional) — server-side data fetching
export let loader = async (args: ComponentLoaderArgs<DataType>) => { ... };import { createSchema } from '@weaverse/hydrogen';
import type { HydrogenComponentProps } from '@weaverse/hydrogen';
interface BannerProps extends HydrogenComponentProps {
heading: string;
description: string;
}
function Banner({ heading, description, children, ...rest }: BannerProps) {
return (
<section {...rest} className="py-16 px-4 text-center">
<h2 className="text-3xl font-bold">{heading}</h2>
<p className="mt-4 text-lg text-gray-600">{description}</p>
{children}
</section>
);
}
export default Banner;
export let schema = createSchema({
type: 'banner',
title: 'Banner',
settings: [
{
group: 'Content',
inputs: [
{ type: 'text', name: 'heading', label: 'Heading', defaultValue: 'Hello World' },
{ type: 'textarea', name: 'description', label: 'Description', defaultValue: 'Welcome to our store.' },
],
},
],
presets: {
heading: 'Hello World',
description: 'Welcome to our store.',
},
});{...rest}{children}childTypesforwardRefforwardRefreftypehero-bannerapp/weaverse/components.tsimport type { HydrogenComponent } from '@weaverse/hydrogen';
// MUST use namespace imports (import * as X), NOT default imports
import * as HeroBanner from '~/sections/hero-banner';
import * as FeaturedCollection from '~/sections/featured-collection';
import * as ProductCard from '~/sections/product-card';
export let components: HydrogenComponent[] = [
HeroBanner,
FeaturedCollection,
ProductCard,
];import HeroBanner from ...import * as HeroBanner from ...createSchema()import { createSchema } from '@weaverse/hydrogen';
export let schema = createSchema({
type: 'my-component', // Unique kebab-case identifier
title: 'My Component', // Display name in Studio
limit: 1, // Max instances per page (optional)
enabledOn: { // Page type restrictions (optional)
pages: ['PRODUCT', 'COLLECTION'],
},
settings: [ // Editor UI groups
{
group: 'Content',
inputs: [
{ type: 'text', name: 'heading', label: 'Heading', defaultValue: 'Title' },
{ type: 'richtext', name: 'body', label: 'Body' },
{ type: 'image', name: 'image', label: 'Image' },
{
type: 'select', name: 'layout', label: 'Layout',
configs: {
options: [
{ value: 'grid', label: 'Grid' },
{ value: 'list', label: 'List' },
],
},
defaultValue: 'grid',
},
],
},
],
childTypes: ['product-card', 'button'], // Allowed child component types
presets: { // Defaults when component is added to page
heading: 'Title',
layout: 'grid',
children: [
{ type: 'product-card' },
{ type: 'product-card' },
],
},
});inspectorsettingsenabledOnINDEXPRODUCTALL_PRODUCTSCOLLECTIONCOLLECTION_LISTPAGEBLOGARTICLECUSTOM| Type | Returns | Use For |
|---|---|---|
| | Single-line text |
| | Multi-line text |
| | Rich text with formatting |
| | URLs/links |
| | Image picker from Shopify Files |
| | Video picker from Shopify Files |
| | Color picker |
| | Slider (requires |
| | Toggle on/off |
| | Dropdown (requires |
| | Button group (requires |
| — | Section header in settings panel (no data) |
| | Date/time picker |
| Shopify product | Product picker |
| Shopify collection | Collection picker |
| Shopify blog | Blog picker |
| Shopify article | Article picker |
| Shopify metaobject | Metaobject picker |
| Shopify products[] | Multi-product picker |
| Shopify collections[] | Multi-collection picker |
import type { ComponentLoaderArgs, HydrogenComponentProps } from '@weaverse/hydrogen';
type MyData = { collectionHandle: string };
export let loader = async ({ weaverse, data }: ComponentLoaderArgs<MyData>) => {
let { storefront } = weaverse;
return await storefront.query(COLLECTION_QUERY, {
variables: { handle: data.collectionHandle },
});
};
// Derive props type from loader return
type Props = HydrogenComponentProps<Awaited<ReturnType<typeof loader>>> & MyData;
function MyComponent({ loaderData, ...rest }: Props) {
let collection = loaderData?.collection;
return <section {...rest}>{collection?.title}</section>;
}
export default MyComponent;weaverse.storefront.query()weaverse.fetchWithCache(url, options)Promise.all([...])shouldRevalidate: trueapp/weaverse/schema.server.ts// app/components/GlobalStyle.tsx
import { useThemeSettings } from '@weaverse/hydrogen';
export function GlobalStyle() {
let settings = useThemeSettings();
if (!settings) return null;
return (
<style dangerouslySetInnerHTML={{ __html: `
:root {
--color-primary: ${settings.colorPrimary};
--body-base-size: ${settings.bodyBaseSize}px;
--heading-base-size: ${settings.headingBaseSize}px;
}
`}} />
);
}import { cva } from 'class-variance-authority';
let buttonVariants = cva('inline-flex items-center rounded font-medium', {
variants: {
variant: { primary: 'bg-blue-600 text-white', secondary: 'bg-gray-200' },
size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4', lg: 'h-12 px-6' },
},
defaultVariants: { variant: 'primary', size: 'md' },
});| API | Purpose |
|---|---|
| Define component schema with Zod validation |
| Server-side client (initialized in |
| Load page data in route loaders |
| Load global theme settings |
| Cached external API fetching |
| HOC wrapping root |
| Access global Weaverse instance |
| Access global theme settings |
| Access a specific component instance |
| Access parent component instance |
| Access child component instances |
import { WeaverseClient } from '@weaverse/hydrogen';
import { components } from '~/weaverse/components';
import { themeSchema } from '~/weaverse/schema.server';
export async function createAppLoadContext(request, env, executionContext) {
let hydrogenContext = createHydrogenContext({ env, request, cache, waitUntil, session, /* ... */ });
return {
...hydrogenContext,
weaverse: new WeaverseClient({
...hydrogenContext,
request,
cache,
themeSchema,
components,
}),
};
}// app/routes/($locale)._index.tsx
import { WeaverseHydrogenRoot } from '@weaverse/hydrogen';
export async function loader({ context }: LoaderFunctionArgs) {
let weaverseData = await context.weaverse.loadPage({ type: 'INDEX' });
return { weaverseData };
}
export default function Homepage() {
return <WeaverseHydrogenRoot />;
}export async function loader({ context, params }: LoaderFunctionArgs) {
let weaverseData = await context.weaverse.loadPage({
type: 'PRODUCT',
handle: params.productHandle,
});
return { weaverseData, /* other data */ };
}| # | File | Topic |
|---|---|---|
| 01 | references/01-project-structure.md | Project structure & file anatomy |
| 02 | references/02-creating-components.md | Component creation & registration |
| 03 | references/03-component-schema.md | createSchema(), settings, childTypes, presets, enabledOn |
| 04 | references/04-input-settings.md | All input types & configurations |
| 05 | references/05-data-fetching.md | Loaders, Storefront API, caching |
| 06 | references/06-styling-theming.md | Tailwind, theme settings, CVA, CSS variables |
| 07 | references/07-react-router-7.md | React Router v7 conventions |
| 08 | references/08-hydrogen-fundamentals.md | Hydrogen framework essentials |
| 09 | references/09-deployment.md | Oxygen, Docker, env vars |
| 10 | references/10-weaverse-api.md | All hooks & WeaverseClient API |
| 11 | references/11-advanced-features.md | Localization, data connectors, CSP |
| 12 | references/12-pilot-theme.md | Pilot theme patterns & conventions |
| 13 | references/13-migration-v5.md | Remix → React Router v7 migration |
| File | Shows |
|---|---|
| examples/hero-banner.tsx | Complete section with schema, settings groups, childTypes, presets |
| examples/featured-collection.tsx | Section with loader, Storefront API query |
| examples/product-card.tsx | Child component example |
| examples/components-registry.ts | Registration pattern |