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

@ashraf8928/section-schema-generator

v2.2.1

Published

CLI tool for React + Vite / Shopify Hydrogen projects. Write Shopify section schemas in JS/TS using export default and generate a consolidated settingSchema.json. Works with both CommonJS and ESM projects.

Readme

@ashraf8928/section-schema-generator

A CLI tool for React + Vite and Shopify Hydrogen projects.

Write your Shopify section schemas in JavaScript or TypeScript using export default — with full IntelliSense and type safety in your editor — and let this tool scan, validate, and generate a consolidated settingSchema.json that follows Shopify's native section schema format exactly.

Works with both CommonJS and ESM projects. No config needed.


Table of Contents


Why use this?

In a standard Shopify Liquid theme, section schemas live inside {% schema %} tags — raw JSON buried inside .liquid files. You get no autocomplete, no validation, no reuse.

This tool lets you write schemas as plain JavaScript objects:

// src/sections/hero/schema.js
export default {
  name: "Hero",
  settings: [
    { type: "text", id: "heading", label: "Heading", default: "Welcome" }
  ]
}

Run one command → get a clean, validated settingSchema.json that your Hydrogen storefront can consume.


Install

npm install --save-dev @ashraf8928/section-schema-generator

That's it. After install, the package automatically adds a generate-schema script to your package.json:

{
  "scripts": {
    "generate-schema": "node ./node_modules/@ashraf8928/section-schema-generator/bin/section-schema.js"
  }
}

You will see this confirmation in your terminal:

✔ [@ashraf8928/section-schema-generator] Added "generate-schema" script to your package.json.
ℹ [@ashraf8928/section-schema-generator] Run it anytime with: npm run generate-schema

If the script already exists, it is never overwritten.


Quick Start

Step 1 — Install the package

npm install --save-dev @ashraf8928/section-schema-generator

Step 2 — Create a section folder with a schema file

src/
  sections/
    hero/
      index.jsx
      schema.js    ← create this

Step 3 — Write your schema

// src/sections/hero/schema.js
export default {
  name: "Hero",
  tag: "section",
  settings: [
    {
      type: "text",
      id: "heading",
      label: "Heading",
      default: "Welcome to our store"
    },
    {
      type: "image_picker",
      id: "background_image",
      label: "Background image"
    }
  ],
  presets: [{ name: "Hero" }]
}

Step 4 — Generate the JSON

npm run generate-schema

Step 5 — Done! Your src/settingSchema.json is generated:

[
  {
    "name": "Hero",
    "tag": "section",
    "settings": [
      {
        "type": "text",
        "id": "heading",
        "label": "Heading",
        "default": "Welcome to our store"
      },
      {
        "type": "image_picker",
        "id": "background_image",
        "label": "Background image"
      }
    ],
    "presets": [{ "name": "Hero" }]
  }
]

Folder Structure

Flat layout — --depth=1 (default)

Each section is a direct subfolder of src/sections. This is the recommended layout for Shopify Hydrogen projects.

src/
  sections/
    hero/
      index.jsx
      schema.js        ← CLI reads this
    slideshow/
      index.jsx
      schema.js        ← CLI reads this
    footer/
      index.jsx
      schema.js        ← CLI reads this

Run:

npm run generate-schema
# or
npx section-schema

Nested layout — --depth=2

Sections grouped under page folders.

src/
  sections/
    home/
      banner/
        schema.js      ← CLI reads this
      carousel/
        schema.js      ← CLI reads this
    product/
      collection-grid/
        schema.js      ← CLI reads this

Run:

npx section-schema --depth=2
# or add it to your package.json script:
# "generate-schema": "section-schema --depth=2"

In both layouts the output is always a flat JSON array — folder structure is only used to find schema files.


Writing a Schema

Basic Example

// src/sections/hero/schema.js

export default {
  name: "Hero",
  tag: "section",
  class: "hero-section",
  settings: [
    {
      type: "text",
      id: "heading",
      label: "Heading",
      default: "Welcome to our store"
    },
    {
      type: "textarea",
      id: "subtext",
      label: "Subtext"
    },
    {
      type: "image_picker",
      id: "background_image",
      label: "Background image"
    },
    {
      type: "color",
      id: "overlay_color",
      label: "Overlay color",
      default: "#000000"
    },
    {
      type: "range",
      id: "overlay_opacity",
      label: "Overlay opacity",
      min: 0,
      max: 100,
      step: 5,
      unit: "%",
      default: 30
    },
    {
      type: "url",
      id: "button_link",
      label: "Button link"
    },
    {
      type: "text",
      id: "button_label",
      label: "Button label",
      default: "Shop now"
    }
  ],
  presets: [
    { name: "Hero" }
  ]
}

With Blocks

// src/sections/slideshow/schema.js

export default {
  name: "Slideshow",
  tag: "section",
  class: "slideshow",
  limit: 1,
  settings: [
    {
      type: "header",
      content: "Slideshow settings"
    },
    {
      type: "checkbox",
      id: "autoplay",
      label: "Auto-rotate slides",
      default: true
    },
    {
      type: "range",
      id: "autoplay_speed",
      label: "Change slides every",
      min: 3,
      max: 9,
      step: 2,
      unit: "s",
      default: 5
    },
    {
      type: "select",
      id: "slide_size",
      label: "Slide size",
      options: [
        { value: "small",  label: "Small"  },
        { value: "medium", label: "Medium" },
        { value: "large",  label: "Large"  }
      ],
      default: "medium"
    }
  ],
  blocks: [
    {
      type: "slide",
      name: "Slide",
      limit: 6,
      settings: [
        {
          type: "image_picker",
          id: "image",
          label: "Image"
        },
        {
          type: "text",
          id: "heading",
          label: "Heading",
          default: "Slide heading"
        },
        {
          type: "select",
          id: "heading_size",
          label: "Heading size",
          options: [
            { value: "h2", label: "Small"  },
            { value: "h1", label: "Medium" },
            { value: "h0", label: "Large"  }
          ],
          default: "h1"
        },
        {
          type: "richtext",
          id: "subheading",
          label: "Subheading"
        },
        {
          type: "text",
          id: "button_label",
          label: "Button label"
        },
        {
          type: "url",
          id: "button_link",
          label: "Button link"
        },
        {
          type: "select",
          id: "button_style",
          label: "Button style",
          options: [
            { value: "primary",   label: "Primary"   },
            { value: "secondary", label: "Secondary" },
            { value: "outline",   label: "Outline"   }
          ],
          default: "primary"
        }
      ]
    }
  ],
  max_blocks: 6,
  presets: [
    {
      name: "Slideshow",
      blocks: [
        { type: "slide" },
        { type: "slide" }
      ]
    }
  ]
}

With Section Groups (enabled_on / disabled_on)

// src/sections/announcement-bar/schema.js

export default {
  name: "Announcement bar",
  tag: "section",
  class: "announcement-bar",
  settings: [
    {
      type: "text",
      id: "text",
      label: "Announcement",
      default: "Free shipping on orders over $50"
    },
    {
      type: "url",
      id: "link",
      label: "Link"
    },
    {
      type: "color",
      id: "bg_color",
      label: "Background color",
      default: "#000000"
    },
    {
      type: "color",
      id: "text_color",
      label: "Text color",
      default: "#ffffff"
    },
    {
      type: "checkbox",
      id: "show_close",
      label: "Show close button",
      default: true
    }
  ],
  enabled_on: {
    templates: ["*"],
    groups:    ["header"]
  }
}

With Locales

// src/sections/hero/schema.js

export default {
  name: "t:sections.hero.name",
  tag: "section",
  settings: [
    {
      type: "text",
      id: "heading",
      label: "t:sections.hero.settings.heading.label",
      default: "t:sections.hero.settings.heading.default"
    },
    {
      type: "richtext",
      id: "body",
      label: "t:sections.hero.settings.body.label"
    }
  ],
  presets: [
    { name: "t:sections.hero.presets.hero.name" }
  ],
  locales: {
    en: {
      sections: {
        hero: {
          name: "Hero",
          presets: { hero: { name: "Hero" } },
          settings: {
            heading: { label: "Heading",     default: "Welcome" },
            body:    { label: "Description"                     }
          }
        }
      }
    },
    fr: {
      sections: {
        hero: {
          name: "Héros",
          presets: { hero: { name: "Héros" } },
          settings: {
            heading: { label: "Titre",       default: "Bienvenue" },
            body:    { label: "Description"                        }
          }
        }
      }
    }
  }
}

With Product / Collection Pickers

// src/sections/featured-collection/schema.js

export default {
  name: "Featured collection",
  tag: "section",
  settings: [
    {
      type: "text",
      id: "title",
      label: "Heading",
      default: "Featured collection"
    },
    {
      type: "collection",
      id: "collection",
      label: "Collection"
    },
    {
      type: "range",
      id: "products_to_show",
      label: "Maximum products to show",
      min: 2,
      max: 25,
      step: 1,
      default: 4
    },
    {
      type: "checkbox",
      id: "show_view_all",
      label: "Show \"View all\" button",
      default: true
    },
    {
      type: "header",
      content: "Product card"
    },
    {
      type: "checkbox",
      id: "show_secondary_image",
      label: "Show second image on hover",
      default: false
    },
    {
      type: "checkbox",
      id: "show_vendor",
      label: "Show vendor",
      default: false
    },
    {
      type: "select",
      id: "image_ratio",
      label: "Image ratio",
      options: [
        { value: "adapt",    label: "Adapt to image" },
        { value: "portrait", label: "Portrait"       },
        { value: "square",   label: "Square"         }
      ],
      default: "adapt"
    }
  ],
  presets: [
    { name: "Featured collection" }
  ]
}

CommonJS Style (module.exports)

If your project uses CommonJS (no "type": "module" in package.json), use module.exports instead:

// src/sections/hero/schema.js

module.exports = {
  name: "Hero",
  tag: "section",
  settings: [
    {
      type: "text",
      id: "heading",
      label: "Heading",
      default: "Welcome"
    }
  ],
  presets: [{ name: "Hero" }]
}

Both styles work. You can even mix them — one section can use export default and another can use module.exports in the same project.


All Supported Setting Types

| Type | Description | |------|-------------| | text | Single-line text input | | textarea | Multi-line text input | | richtext | Rich text editor (supports block-level HTML) | | inline_richtext | Inline rich text (no block elements like <p>) | | image_picker | Image selector from the Shopify media library | | video | Shopify-hosted video selector | | video_url | YouTube or Vimeo URL input | | url | URL picker — internal pages or external links | | checkbox | Boolean toggle (true / false) | | radio | Radio button group (requires options array) | | select | Dropdown select (requires options array) | | range | Numeric slider (requires min, max, step) | | number | Freeform numeric input | | color | Color picker (hex value) | | color_background | Solid color or gradient picker | | color_scheme | Color scheme selector | | color_scheme_group | Color scheme group selector | | font_picker | Font selector from Shopify's font library | | html | Raw HTML textarea | | liquid | Liquid code textarea | | article | Article resource picker | | blog | Blog resource picker | | collection | Single collection picker | | collection_list | List of collections picker | | page | Page resource picker | | product | Single product picker | | product_list | List of products picker | | link_list | Navigation menu picker | | metaobject | Single metaobject entry picker | | metaobject_list | List of metaobject entries picker | | header | Informational header divider (no id required) | | paragraph | Informational paragraph text (no id required) |


All Top-Level Schema Keys

| Key | Type | Required | Description | |-----|------|----------|-------------| | name | string | ✅ Yes | Section name shown in the Shopify theme editor | | tag | string | No | HTML element for the section wrapper ("section", "div", "aside", "article", "footer", "header") | | class | string | No | CSS class added to the section wrapper element | | limit | number | No | Max number of times this section can be added per template | | settings | array | No | Array of setting objects (see setting types above) | | blocks | array | No | Array of block type definitions (each block can have its own settings) | | max_blocks | number | No | Maximum total block instances allowed | | presets | array | No | Default configurations shown in the "Add section" panel | | locales | object | No | Inline translation strings (keyed by locale code) | | enabled_on | object | No | Restricts which templates or section groups can use this section | | disabled_on | object | No | Prevents this section on specific templates or groups |


Run Commands

Generate the schema (normal)

npm run generate-schema

Generate with npx (no script needed)

npx section-schema

Custom source folder

npx section-schema --src app/sections

Custom output file

npx section-schema --out app/schema.json

Custom source and output

npx section-schema --src app/sections --out app/schema.json

Nested page/section layout (depth 2)

npx section-schema --depth=2

Everything custom

npx section-schema --src=app/sections --out=dist/schema.json --depth=2

Add to package.json scripts manually

{
  "scripts": {
    "generate-schema":      "section-schema",
    "generate-schema:watch": "section-schema --src src/sections --out src/settingSchema.json"
  }
}

Then run:

npm run generate-schema

Show help

npx section-schema --help

Output:

section-schema

Scans your project's section folders for schema.js / schema.ts / schema.json
files and generates a consolidated settingSchema.json array that follows
Shopify's native section schema format.

Usage:
  section-schema [options]

Options:
  --src <path>     Path to the sections root directory (default: src/sections)
  --out <path>     Path to write the generated JSON file (default: src/settingSchema.json)
  --depth <1|2>    Folder scan depth:
                     1 = flat:   src/sections/<section>/schema.js  (default)
                     2 = nested: src/sections/<page>/<section>/schema.js
  -h, --help       Show this help message

CLI Options

| Flag | Default | Description | |------|---------|-------------| | --src <path> | src/sections | Path to the sections root directory | | --out <path> | src/settingSchema.json | Path to write the generated JSON file | | --depth <1\|2> | 1 | 1 = flat layout, 2 = page/section nested layout | | -h, --help | — | Show usage help |

All paths resolve relative to wherever you run the command from (process.cwd()).


Generated Output

Every schema.js file becomes one entry in the output array. The output file is always overwritten on each run so it stays in sync with your source files.

Example — two sections:

src/sections/
  hero/schema.js
  slideshow/schema.js

Generates src/settingSchema.json:

[
  {
    "name": "Hero",
    "tag": "section",
    "settings": [
      {
        "type": "text",
        "id": "heading",
        "label": "Heading",
        "default": "Welcome to our store"
      },
      {
        "type": "image_picker",
        "id": "background_image",
        "label": "Background image"
      }
    ],
    "presets": [
      { "name": "Hero" }
    ]
  },
  {
    "name": "Slideshow",
    "tag": "section",
    "class": "slideshow",
    "limit": 1,
    "settings": [
      {
        "type": "checkbox",
        "id": "autoplay",
        "label": "Auto-rotate slides",
        "default": true
      }
    ],
    "blocks": [
      {
        "type": "slide",
        "name": "Slide",
        "settings": [
          {
            "type": "image_picker",
            "id": "image",
            "label": "Image"
          }
        ]
      }
    ],
    "presets": [
      { "name": "Slideshow" }
    ]
  }
]

TypeScript Support

Install jiti as a dev dependency:

npm install --save-dev jiti

Then write your schemas in TypeScript:

// src/sections/hero/schema.ts

export default {
  name: "Hero",
  tag: "section" as const,
  settings: [
    {
      type: "text" as const,
      id: "heading",
      label: "Heading",
      default: "Welcome"
    },
    {
      type: "image_picker" as const,
      id: "bg_image",
      label: "Background image"
    }
  ],
  presets: [{ name: "Hero" }]
}

Or with a typed interface for full IntelliSense:

// src/sections/slideshow/schema.ts

interface Setting {
  type: string
  id?: string
  label?: string
  default?: unknown
  [key: string]: unknown
}

interface Block {
  type: string
  name: string
  settings?: Setting[]
}

interface SectionSchema {
  name: string
  tag?: string
  class?: string
  limit?: number
  settings?: Setting[]
  blocks?: Block[]
  max_blocks?: number
  presets?: { name: string }[]
}

const schema: SectionSchema = {
  name: "Slideshow",
  tag: "section",
  class: "slideshow",
  limit: 1,
  settings: [
    { type: "checkbox", id: "autoplay", label: "Auto-rotate slides", default: true }
  ],
  blocks: [
    {
      type: "slide",
      name: "Slide",
      settings: [
        { type: "image_picker", id: "image", label: "Image" }
      ]
    }
  ],
  presets: [{ name: "Slideshow" }]
}

export default schema

Run the same command:

npm run generate-schema

If jiti is not installed, schema.ts files are skipped with a warning and the CLI never crashes:

⚠ Found "src/sections/hero/schema.ts" but no TypeScript loader is installed.
  Install jiti to enable schema.ts support:
  npm install --save-dev jiti

Validation

The CLI validates every schema before writing the output. If validation fails, the run stops and every problem is listed clearly:

✖ Schema validation failed with 3 error(s):
  • src/sections/hero/schema.js: schema is missing required "name" field.
  • src/sections/slideshow/schema.js: duplicate setting id "heading" in settings.
  • src/sections/slideshow/schema.js: blocks[0]: missing required "type" field.

Fix the errors and re-run:

npm run generate-schema

Warnings (non-blocking) are printed but never stop the build. Example: using a setting type that was introduced in a newer version of Shopify and isn't yet in this package's known-types list — it gets a warning but still passes through to the output as-is.


Backward Compatibility

schema.json files still work. If you have existing JSON schema files, they are loaded as-is.

Priority order when multiple files exist in the same folder:

| Priority | File | Notes | |----------|------|-------| | 1st | schema.js | ESM or CJS — always preferred | | 2nd | schema.ts | Requires jiti installed | | 3rd | schema.json | Legacy fallback |


Architecture

bin/
  section-schema.js    # CLI entrypoint — args, help text, error handling, exit code
  postinstall.js       # Runs after npm install — adds generate-schema script automatically
lib/
  generateSchema.js    # Orchestrator — resolves paths, calls scan, writes output, logs
  scanSections.js      # Discovers all section folders, returns validated schema array
  schemaLoader.js      # Loads schema.js (ESM+CJS) / schema.ts (jiti) / schema.json
  validator.js         # Validates each schema object against Shopify's section schema spec
utils/
  argsParser.js        # Parses --src / --out / --depth / --help from process.argv
  fileUtils.js         # isDirectory, isFile, listSubdirectories, writeJsonFile
  logger.js            # ANSI-colored console output — success, error, warn, info
test/
  run.js               # 31 tests using node:assert only — no test framework needed

Each module has a single responsibility. To add a new file type (e.g. schema.yaml), only schemaLoader.js needs to change. To add a new validation rule, only validator.js needs to change.


License

MIT