Featured Product (Shopify)
A Featured Product card block powered by the Shopify Storefront API.
by Development and Digital · github.com/iamrmrishan/wp-shopify-featured-product
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/iamrmrishan/wp-shopify-featured-product/archive/refs/heads/main.zipFeatured Product (WordPress + React + Shopify)
A lean WordPress plugin that renders a Featured Product card on any page. Product data comes from the Shopify Storefront API; the card is a TypeScript React component inside a native Gutenberg block.
Integration method (and why)
Custom plugin → PHP fetches Shopify → SSR HTML → React hydrates → cart via a small REST proxy → Shopify hosted checkout.
| Approach | Why not (for this task) |
|---|---|
| Sell on WordPress (official) | Right for a real client; this assessment asks for your React + Storefront build |
| Buy Button / JS embed | Product HTML is injected after JS — poor SEO, stale SDK |
| Client-side Storefront calls | Token in the page + extra request waterfall after paint |
| ShopWP / page builders | Stacked plugins; against the brief’s restraint |
What we chose instead
- PHP calls Storefront GraphQL (
productquery) and caches the result in a WP transient (10 min). render.phpoutputs real HTML (title, price, image) plusdata-productJSON for React.view.tsxhydratesFeaturedProductCard— UI copy and demo fallback live insrc/config.ts.- Add to cart posts to
POST /wp-json/featured-product/v1/cart(WP nonce). PHP runscartCreate/cartLinesAdd, stores the cart id in an HttpOnly cookie, returnscheckoutUrl. - Checkout is Shopify’s hosted checkout — we do not build our own.
The Storefront token stays in Settings → Featured Product (wp_options), never in JavaScript. For production, Shopify also supports a private Storefront token with a Shopify-Storefront-Buyer-IP header on server requests; this repo uses the public token in PHP only to keep Partner-store setup to one field.
Requirements
- Node.js 18+
- Docker (for
@wordpress/env) - A free Shopify Partner development store (optional for demo fallback)
Quick start
cd wp-shopify-featured-product
npm install
npm run build
npx wp-env start
- Site: http://localhost:8888
- Admin: http://localhost:8888/wp-admin — user
admin, passwordpassword
Shopify setup
- Create a Partner development store and add at least one product.
- In Shopify admin: Settings → Apps and sales channels → Develop apps (or install the Headless channel).
- Create a Storefront API access token with scopes for:
- Read products (
unauthenticated_read_product_listingsor equivalent) - Cart / checkout write (
unauthenticated_write_checkoutsand cart scopes)
- Read products (
- Copy the product handle from the URL (
/products/your-handle).
WordPress setup
- Settings → Featured Product — enter shop domain, token, and handle.
- Create a page and insert the Featured Product block (or shortcode
[featured_product]). - View the page — live title, price, and image when Shopify is configured; demo fallback otherwise.
- Click Add to cart, then Checkout to open Shopify’s test checkout (use bogus gateway on dev stores).
Development
npx wp-env start
npm run start # webpack watch for src/
Stop environment: npx wp-env stop
Project structure
featured-product.php Plugin bootstrap
includes/
class-shopify-client.php Storefront GraphQL (product + cart)
class-cart-rest.php POST /cart REST endpoint
class-settings.php Admin settings
class-block.php Block + shortcode SSR
src/
block.json Block metadata
render.php Server-side render
index.tsx Block editor
view.tsx Frontend hydrate
featured-product-card.tsx React card UI
config.ts Copy + fallback product (imported by card)
style.css
build/ Compiled assets (commit after build)
Development plan
This is the Cursor plan used to build the plugin (4–6 hour hiring scope). Todos below were completed as listed.
Overview: TypeScript React Featured Product card in WordPress. PHP SSR/caches Storefront product data; React renders the card and adds the first variant to a Storefront cart, then Shopify hosted checkout.
- [x] Scaffold plugin bootstrap,
block.json,.wp-env.json,package.json,tsconfig, gitignore - [x] Shopify GraphQL client (
product+cartCreate/cartLinesAdd), transients for product only, settings,render.phpdata-product, REST POST cart + cart-id cookie - [x] TypeScript card +
src/config.ts(copy, fallback product); hydrate fromdata-product; Add to cart then Checkout; editorServerSideRender - [x] README (run + method + why, including cart) and
DECISIONS.md(three half-page answers)
Assessment fit
Cart is in the brief as a bonus; this plan treats Add to cart + hosted checkout as required, still scoped to one product / first variant so it stays inside 4–6 hours.
| Brief | Plan |
|---|---|
| Single Featured Product card | One product, one card, one handle in settings |
| React component inside a WordPress page | FeaturedProductCard mounts via a native block (plus a thin shortcode alias) |
| Live title, price, image | Storefront product(handle:) fields |
| Shopify Storefront API | PHP GraphQL to {shop}/api/2026-07/graphql.json |
| Integration method is your call | Custom plugin; README explains why vs official plugin / Buy Button |
| Cart/checkout | Required: Storefront Cart API → Shopify checkoutUrl |
| Core = live data rendering cleanly | PHP puts product in the HTML; React renders the card from that payload |
| README: how to run + method + why | Yes |
| Decisions doc, ½ page × 3 | DECISIONS.md |
| 4–6 hours, restraint, no page builders | One plugin, no Elementor/Divi, no stacked Shopify plugins |
| Free tools, Partner store, local WP | wp-env (Docker), free React/@wordpress/scripts, Partner store placeholders |
| Note assumptions | README |
Not contradictions:
- PHP calls Storefront for product + cart writes; React renders and posts Add to cart. Token stays off the page. Cart is buyer state (cookie + REST), not cached like the product.
- Gutenberg is not a page builder. One core block is the WP-native mount.
config.tsfallback vs “live data”. Live Shopify wins when settings are filled; fallback is unconfigured local demo. Add to cart is disabled or errors cleanly without a real variant GID.DECISIONS.mdrecommending Sell on WordPress for a real client does not fight the build.
Keep React as the card UI (title, price, image, add to cart). PHP is host + Storefront + product cache + cart proxy.
What we chose not to copy from the web
- Sell on WordPress (official) — default for a real merchant; wrong for this brief (they asked us to build React + Storefront API).
- Buy Button / JS Buy SDK — client-injected card, not indexable. Do not ship a JS-only product card.
- Custom Storefront API — query GraphQL, cache product in WP transients, inject HTML in PHP. Cart: Storefront Cart API then Shopify hosted
checkoutUrl. Do not build a custom checkout.
Shopify.dev Storefront auth: public token is for the browser; private token + buyer IP is for servers. This plugin keeps the public token in PHP only (one settings field). README notes the private-token pattern for production.
Integration method (plan)
Custom dynamic Gutenberg block. PHP fetches/caches the product (SSR). React renders the card. Add to cart goes through a small WP REST proxy to the Storefront Cart API, then Shopify checkout.
- Settings: shop domain, Storefront token, product handle.
- Block
featured-product/card:render.phpqueries product (cached ~10 min), prints card shell +data-productJSON includingvariantId(first variant GID) andavailable. - TypeScript
viewScriptmountsFeaturedProductCard. Copy +fallbackProductfromsrc/config.ts. - Editor:
@wordpress/server-side-render. Add to cart is frontend-only. - Product read: no REST. Cart write:
POST /wp-json/featured-product/v1/cartwith WP nonce. PHPcartCreateorcartLinesAdd, cart id in an HttpOnly cookie, response{ checkoutUrl, totalQuantity }. Expired cart → create a new one. - Token in
wp_options. Public Storefront token header. API version2026-07. - Thin
[featured_product]shortcode alias.
Cart scope (restraint): one Add to cart control, quantity default 1; Checkout uses Shopify checkoutUrl; first variant only. No variant picker, no mini-cart, no Customer Account API.
flowchart LR
Editor["Gutenberg block"]
PHP["render.php + product transient"]
Shopify["Storefront GraphQL"]
HTML["SSR + data-product"]
React["TS card"]
CartRest["POST cart REST"]
Checkout["Shopify checkoutUrl"]
Editor --> PHP
PHP --> Shopify
PHP --> HTML
HTML --> React
React --> CartRest
CartRest --> Shopify
CartRest --> Checkout
Product query: title, featuredImage { url altText }, priceRange.minVariantPrice { amount currencyCode }, onlineStoreUrl, variants(first: 1) { nodes { id availableForSale } }.
Cart mutations: cartCreate / cartLinesAdd with merchandiseId; return checkoutUrl and totalQuantity.
Out of scope
No cart drawer, cart page, variant picker, Customer Account API, custom checkout, tests, Tailwind/page builder, product picker, Hydrogen/Next.
Verify
Placeholders: settings save; card shows fallback from config.ts; Add to cart errors or stays disabled without a live variant. With a real store: SSR title/price/image; Add to cart returns checkoutUrl; Checkout opens Shopify checkout.
Assumptions
- One featured product (first variant only; no variant picker).
- English UI strings in
config.ts. - Docker available for
wp-env(Local WP / Studio also work if you copy the plugin intowp-content/plugins). - Partner-store checkout uses Shopify’s test payment flow, not real charges.
config.tsprovides demo data when Shopify is not configured; live fields replace it once settings are saved.
Loom talk track (~3–5 min)
- Goal — one card, three systems: React, WordPress, Shopify.
- Why custom plugin — assessment wants your integration; official plugin is the real-client default (see
DECISIONS.md). - Data flow — settings → PHP GraphQL + transient → SSR HTML → React hydrate from
data-product. config.ts— copy and fallback; secrets stay in WP settings.- Cart — REST proxy, cookie, Storefront Cart API, hosted checkout.
- Trade-offs — no cart drawer/page; first variant only; scoped for 4–6 hours.
License
GPL-2.0-or-later