npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nextshopkit/sdk

v0.5.2

Published

NextShopKit PRO SDK for Next.js + Shopify

Readme

🛍️ NextShopKit SDK

npm version TypeScript Next.js License: MIT

A modern, typed, and opinionated SDK for building Shopify headless storefronts with Next.js. Skip the boilerplate. Stop wrestling with GraphQL. Start shipping faster.

🚀 Why NextShopKit?

Building a Shopify headless store from scratch is hard. You'll run into:

  • ❌ Complex GraphQL queries
  • ❌ Untyped responses
  • ❌ Confusing metafields
  • ❌ Repeating the same code over and over

NextShopKit gives you:

  • Prebuilt, typed functions for common operations
  • Metafield parsing, filter handling, and cart utilities
  • Ready for React – use as hooks or server-side
  • Full TypeScript support with intelligent autocomplete
  • Built-in caching for optimal performance

📦 Installation

npm install @nextshopkit/sdk
yarn add @nextshopkit/sdk
pnpm add @nextshopkit/sdk

🔧 Quick Setup

1. Environment Variables

Add to your .env.local:

SHOPIFY_ACCESS_TOKEN=your-storefront-access-token
SHOPIFY_STORE_DOMAIN=your-shop.myshopify.com

2. Initialize Client

// lib/nextshopkit/client.ts
import { createShopifyClient } from "@nextshopkit/sdk";

const client = createShopifyClient({
  shop: process.env.SHOPIFY_STORE_DOMAIN!,
  token: process.env.SHOPIFY_ACCESS_TOKEN!,
  apiVersion: "2025-04",
  enableMemoryCache: true,
  defaultCacheTtl: 300,
  enableVercelCache: true,
  defaultRevalidate: 60,
});

export const getProduct = async (args) => client.getProduct(args);
export const getCollection = async (args) => client.getCollection(args);
export const getSearchResult = async (args) => client.getSearchResult(args);

export default client;

3. Use in Server Components

// app/product/[handle]/page.tsx
import { getProduct } from "@/lib/nextshopkit/client";

export default async function ProductPage({ params }) {
  const { data, error } = await getProduct({
    handle: params.handle,
    customMetafields: [
      { field: "custom.warranty", type: "single_line_text" },
      { field: "custom.weight", type: "weight" },
    ],
  });

  if (error || !data) {
    return <div>Product not found</div>;
  }

  return (
    <div>
      <h1>{data.title}</h1>
      <p>
        ${data.price.amount} {data.price.currencyCode}
      </p>
      <div dangerouslySetInnerHTML={{ __html: data.descriptionHtml }} />

      {/* Access custom metafields */}
      <p>Warranty: {data.metafields.customWarranty}</p>
      <p>
        Weight: {data.metafields.customWeight?.value}{" "}
        {data.metafields.customWeight?.unit}
      </p>
    </div>
  );
}

🧩 Core Features

🛍️ Product Management

// Fetch single product
const { data, error } = await getProduct({
  handle: "premium-t-shirt",
  customMetafields: [
    { field: "custom.material", type: "single_line_text" },
    { field: "custom.care_instructions", type: "rich_text" },
  ],
  options: {
    camelizeKeys: true,
    renderRichTextAsHtml: true,
  },
});

// Fetch collection with filters
const collection = await getCollection({
  handle: "summer-collection",
  first: 20,
  filters: [
    { price: { min: 10, max: 100 } },
    { available: true },
    { tag: "organic" },
  ],
  sortKey: "PRICE",
  reverse: false,
});

🔍 Advanced Search

// Search across products, collections, articles
const searchResults = await getSearchResult({
  query: "organic cotton",
  types: ["PRODUCT", "COLLECTION"],
  first: 10,
  filters: [{ price: { min: 20, max: 200 } }, { productType: "Clothing" }],
  sortKey: "RELEVANCE",
});

🛒 Cart Management

// Provider setup (app/layout.tsx)
import { CartProvider } from "@nextshopkit/sdk/client";
import { createShopifyClient } from "@nextshopkit/sdk";

// Client-side client for cart operations
const cartClient = createShopifyClient({
  shop: process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN!,
  token: process.env.NEXT_PUBLIC_SHOPIFY_ACCESS_TOKEN!,
  apiVersion: "2025-04",
});

export default function RootLayout({ children }) {
  return <CartProvider client={cartClient}>{children}</CartProvider>;
}

// Use in components
import { useCart } from "@nextshopkit/sdk/client";

function AddToCartButton({ variantId }) {
  const { addProducts, loading, cart } = useCart();

  return (
    <button
      onClick={() => addProducts([{ merchandiseId: variantId, quantity: 1 }])}
      disabled={loading}
    >
      Add to Cart ({cart?.totalQuantity || 0})
    </button>
  );
}

🏷️ Metafields Made Easy

// Define metafields with types
const product = await getProduct({
  handle: "laptop",
  customMetafields: [
    { field: "custom.processor", type: "single_line_text" },
    { field: "custom.ram", type: "number_integer" },
    { field: "custom.specifications", type: "rich_text" },
    { field: "custom.warranty_document", type: "file_reference" },
  ],
  options: {
    camelizeKeys: true,
    resolveFiles: true,
    renderRichTextAsHtml: true,
    transformMetafields: (raw, casted) => ({
      ...casted,
      // Create computed fields
      displaySpecs: `${casted.customProcessor} • ${casted.customRam}GB RAM`,
      warrantyYears: casted.customWarrantyDocument ? "2 years" : "1 year",
    }),
  },
});

// Access transformed metafields
console.log(product.data.metafields.displaySpecs);
console.log(product.data.metafields.warrantyYears);

🎯 Advanced Usage

Filtering & Pagination

// Advanced collection filtering
const filteredProducts = await getCollection({
  handle: "electronics",
  first: 12,
  after: "cursor-from-previous-page",
  filters: [
    { price: { min: 100, max: 1000 } },
    { available: true },
    { variantOption: { name: "Color", value: "Black" } },
    { metafield: { namespace: "custom", key: "brand", value: "Apple" } },
  ],
  sortKey: "PRICE",
  reverse: false,
  customMetafields: [
    { field: "custom.brand", type: "single_line_text" },
    { field: "custom.rating", type: "rating" },
  ],
});

Server Actions Integration

// app/actions/cart.ts
"use server";

import { getProduct } from "@/lib/nextshopkit/client";

export async function addToCartAction(
  productHandle: string,
  variantId: string
) {
  const { data, error } = await getProduct({ handle: productHandle });

  if (error || !data) {
    throw new Error("Product not found");
  }

  // Add to cart logic here
  return { success: true, product: data };
}

API Routes

// app/api/products/route.ts
import { getCollection } from "@/lib/nextshopkit/client";
import { NextRequest } from "next/server";

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams;
  const handle = searchParams.get("collection");

  const { data, error } = await getCollection({
    handle: handle || "all",
    first: 20,
  });

  if (error) {
    return Response.json({ error }, { status: 500 });
  }

  return Response.json({ products: data.products });
}

🔒 Security & Best Practices

🔐 Secure Credential Usage

Use appropriate environment variables for your use case:

Server-side operations (recommended for most data fetching):

SHOPIFY_ACCESS_TOKEN=your-storefront-access-token
SHOPIFY_STORE_DOMAIN=your-shop.myshopify.com

Client-side operations (required for cart functionality):

NEXT_PUBLIC_SHOPIFY_ACCESS_TOKEN=your-storefront-access-token
NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN=your-shop.myshopify.com

🛡️ Security Guidelines

For Server-side Usage:

  • Use private environment variables (without NEXT_PUBLIC_ prefix)
  • Keep credentials secure and never expose to client bundles
  • Use in Server Components, API routes, and "use server" functions

For Client-side Usage (Cart functionality):

  • Use NEXT_PUBLIC_ prefixed environment variables
  • Only use Storefront API tokens (never Admin API tokens)
  • Ensure your Storefront API token has minimal required permissions
  • Consider implementing rate limiting and request validation

⚖️ When to Use Each Approach

Server-side (Recommended for data fetching):

  • Product pages
  • Collection pages
  • Search results
  • Static generation
  • Better performance and SEO

Client-side (Required for interactive features):

  • Cart management (CartProvider, useCart())
  • Real-time inventory updates
  • Interactive product configurators
  • Dynamic user-specific content

🚀 Performance Optimization

// Enable caching for better performance
const client = createShopifyClient({
  shop: process.env.SHOPIFY_STORE_DOMAIN!,
  token: process.env.SHOPIFY_ACCESS_TOKEN!,
  apiVersion: "2025-04",
  enableMemoryCache: true, // In-memory caching
  defaultCacheTtl: 300, // 5 minutes
  enableVercelCache: true, // Vercel ISR caching
  defaultRevalidate: 60, // 1 minute revalidation
});

📊 Available Methods

| Method | Description | Tier | | ------------------- | ---------------------------------------- | ---- | | getProduct() | Fetch single product by handle/ID | Core | | getCollection() | Fetch collection with products & filters | Core | | getSearchResult() | Search products, collections, articles | Core | | getPolicies() | Fetch shop policies | Core | | Cart Functions | Complete cart management | Core |

🚀 PRO Tier Features

Upgrade to @nextshopkit/pro for advanced features:

  • 🎯 getProductVariant() - Fetch single variant with product context
  • 🎯 getProductVariants() - Bulk variant fetching
  • 🎯 getPolicy() - Fetch specific shop policy
  • 📝 Metaobjects support
  • 📰 Blog posts & articles
  • 🤖 Product recommendations
  • 🌍 Localization support
  • 🔍 Advanced search features

📚 Documentation

🆚 Why Not Hydrogen?

Hydrogen is great, but it comes with constraints:

  • Built around Vite and custom tooling
  • Smaller community and ecosystem
  • Learning curve for teams familiar with Next.js

NextShopKit lets you:

  • ✅ Stay in Next.js (most popular React framework)
  • ✅ Deploy anywhere (Vercel, AWS, Cloudflare)
  • ✅ Leverage massive ecosystem and talent pool
  • ✅ Use familiar patterns and tooling

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

MIT © NextShopKit

🔗 Links


Built with ❤️ for the Next.js and Shopify community