3U

Project Setup (threeu.json)

How the CLI identifies a theme or plugin project, and what the manifest must contain.

Scaffolding a Project

Use your usual toolchain — the CLI does not generate projects. Published themes are Vite + React apps:

Theme
Shell
npm create vite@latest my-theme -- --template react-ts
cd my-theme
npm install threeu-sdk

threeu.json

The CLI detects the project from a threeu.json file at the project root (or a "threeu" field in package.json). It reads the file verbatim — it does not execute defineTheme() — so the manifest must be written here by hand:

threeu.json
JSON
{
  "kind": "theme",
  "key": "modern-luxury",
  "name": "Modern Luxury",
  "version": "1.0.0",
  "manifest": {
    "type": "theme",
    "name": "Modern Luxury",
    "slug": "modern-luxury",
    "version": "1.0.0",
    "visibility": "public",
    "locales": ["ar", "en"],
    "default_locale": "ar",
    "pages": ["home", "products", "product", "cart", "checkout", "about", "contact"],
    "editable_fields": [
      { "name": "hero.heading", "kind": "heading", "label": "Hero heading" },
      { "name": "hero.image", "kind": "image", "label": "Hero background image" }
    ],
    "capabilities": {
      "catalog":  { "required": true,  "features": { "products": true, "collections": true }, "modes": ["dynamic"] },
      "checkout": { "required": false, "features": { "delivery": true, "payments": true, "coupons": true }, "modes": ["dynamic"] },
      "leads":    { "required": true,  "features": { "contact": true }, "modes": ["dynamic"] }
    }
  }
}

Fields

FieldRule
kind"theme" or "plugin"
keyLowercase letters/numbers with - or _; unique per developer. Another developer's key → 422 on publish
nameDisplay name
versionSemver; re-publishing the same key+version creates a new submission
manifestRequired for themes. A bare {kind,key,name,version} publishes with manifest: null and fails review

The reviewer's blocking manifest checks are exactly three: `manifest_present` (non-empty object), `manifest_pages` (≥ 3 entries), `manifest_locales` (non-empty array). Keep manifest.pages identical to the list you pass to defineTheme({ pages }). Plugin submissions must use the slug of a plugin you already own in the Developer Console.

Vite Alias & Preview Build

Two vite.config settings the review pipeline depends on: build to preview-dist/ with relative asset URLs (the reviewer renders that folder in an iframe), and declare the @src alias if your code uses it (an alias a generator once provided is not provided by the CLI):

vite.config.ts
TypeScript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { fileURLToPath, URL } from 'node:url'

export default defineConfig({
  plugins: [react()],
  resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } },
  base: './',
  build: { outDir: 'preview-dist' },
})