Project structure
A folder-by-folder tour of souk-theme/. Read once, refer back when you need to remember "where do X live?"
souk-theme/
├── app.vue Root component. Renders global Header/Footer + the active page.
├── error.vue Renders when Nuxt catches a route error.
├── nuxt.config.ts Nuxt configuration — runtime config, modules, build settings.
├── tailwind.config.js Tailwind setup — maps utility classes to design tokens.
├── tsconfig.json TypeScript config.
├── package.json Dependencies + npm scripts.
├── Dockerfile Production container build (for the dev/prod Node server).
├── README.md Quick start (you're past this).
├── DEPLOY.md Deploy notes for the team.
├── .env.example Template for local env vars. Copy to .env.
│
├── components/ All Vue components.
│ ├── sections/ ← where you spend 95% of your time.
│ │ ├── Banner/
│ │ ├── Heading/
│ │ ├── Header/
│ │ ├── Footer/
│ │ ├── ProductGrid/
│ │ └── ... (50+ sections)
│ ├── TemplateSections.vue The renderer that walks templates[page] and instantiates each section.
│ ├── ProductCard.vue Shared product-card UI used by grid sections.
│ └── ... (other shared UI components)
│
├── pages/ Nuxt routes — one file per public URL.
│ ├── index.vue / (home)
│ ├── shop.vue /shop (catalog)
│ ├── cart/ /cart (cart pages)
│ ├── wishlist.vue /wishlist
│ ├── products/[slug].vue /products/:slug
│ ├── collections/ /collections, /collections/:slug
│ ├── blog.vue /blog
│ ├── posts/[slug].vue /posts/:slug
│ ├── pages/[slug].vue /pages/:slug (CMS pages)
│ ├── thanks.vue /thanks (order confirmation)
│ ├── search.vue /search
│ └── account/ /account, /account/orders, etc.
│
├── plugins/ Boot-time wiring.
│ ├── init.ts The big one — fetches the active theme on SSR,
│ │ sets up Pinia state, decides builder mode.
│ ├── http.ts Axios client. Resolves the auth token from
│ │ URL/cookie/env (see Getting started → Connecting to a store).
│ ├── storeino.ts The `$storeino` API client used in data-driven sections.
│ ├── bridge.client.ts PostMessage bridge to the builder. Only matters in builder mode.
│ ├── theme-vars.client.ts Emits the merchant's tokens as :root CSS variables.
│ ├── events.client.ts Cart / wishlist event bus.
│ ├── pixels.client.ts Facebook/Google/TikTok pixel boot.
│ ├── page-view.client.ts Pageview tracking.
│ ├── animations.client.ts Intersection-observer-driven entrance animations.
│ ├── currency-modal.client.ts First-visit currency picker.
│ ├── resize-handles.client.ts Builder-mode resize handles on images.
│ ├── sanitize-payload.server.ts SSR safety: scrub null-prototype objects.
│ └── tools.ts Misc helpers exposed on $tools.
│
├── composables/ Reusable hooks. Most you'll touch:
│ ├── useT.ts Translate a value (string or locale map).
│ ├── useStyles.ts Pick up merchant style edits per element.
│ ├── useEditable.ts Click-to-edit text in builder mode.
│ ├── useThemeStore Alias for the Pinia theme store.
│ └── use*Context.ts Per-page data adapters (CartContext, BlogContext, ...).
│ Used by page-aware sections to read what page they're on.
│
├── stores/ Pinia stores.
│ ├── theme.ts The theme document — sections, templates, tokens.
│ │ Mutated by patches from the builder.
│ └── main.ts Shopper state — cart, wishlist, language, currency.
│
├── utils/ Pure helpers (no Vue, no Pinia).
│ ├── sectionRegistry.ts Auto-discovers components/sections/* at boot.
│ ├── elementRegistry.ts Same idea for sub-element components.
│ ├── initializePixels.ts Pixel boot helper.
│ └── storeinoApp.ts Internal init helper.
│
├── types/ Shared TypeScript types.
│ └── section.ts SectionInstance, SectionManifest, ValueSchema, etc.
│
├── public/ Static assets served at /...
│ ├── sections/ ← Auto-generated. Don't edit by hand.
│ │ └── manifest-index.json
│ ├── elements/
│ └── templates/
│
├── server/ Nitro server routes + middleware (Nuxt's server side).
│ └── middleware/ Per-request middleware (auth header reshaping, etc.).
│
├── assets/ Source assets bundled by Vite.
│ └── css/ main.css (Tailwind imports + token CSS).
│
├── scripts/ Build / deploy / maintenance Node scripts.
│ ├── build-manifest-index.mjs Regenerates public/sections/manifest-index.json.
│ ├── build-chrome-bundles.mjs Builds Header/Footer bundles for the builder.
│ ├── build-global-bundles.mjs Builds the global-slot manifest.
│ ├── seed-registry.mjs Pushes the section catalog to api-stores.
│ ├── upload-cdn.mjs Uploads .output/public/ to BunnyCDN.
│ ├── swap-brand-thumbs.mjs Maintenance: refresh thumbnail URLs.
│ └── patch-broken-esm.mjs Postinstall workaround for one broken dep.
│
└── docs/ Internal team docs (markdown, not the public site).Where you spend your time
For section work, you'll mostly touch:
| Folder | When |
|---|---|
components/sections/<NewSection>/ | Every section you build lives here. |
composables/ | Read useT, useStyles, useEditable for reference. Rarely add new ones. |
components/ProductCard.vue | Read if you're building a product-data section. Don't modify without checking with Abdel — it's shared. |
assets/css/main.css | Almost never. Global tweaks only. Section CSS lives inline as Tailwind classes. |
Where you almost never go
| Folder | Why |
|---|---|
plugins/ | Boot infrastructure. Modifying these affects every page. |
stores/theme.ts | The shape of the theme document. Modifying it touches the builder + api-stores model. |
scripts/ | Deploy machinery. Run them via npm run build / npm run deploy, don't edit. |
server/middleware/ | Per-request SSR hooks. Owned by infra. |
nuxt.config.ts | Boot config. Adding a new dependency that needs Nuxt config is the only legitimate reason. |
types/section.ts | The SectionInstance / SectionManifest shapes. If you need to add a new field type (e.g. gradient), this is where it goes — but ping Abdel first because the builder also needs the matching editor. |
How sections wire into pages
This is the single most-asked structural question:
pages/index.vue (the home route)
└─ <TemplateSections template="home" />
└─ Reads themeStore.theme.config.templates.home (an array of SectionInstance)
└─ For each instance:
resolveSection(instance.type) (looks up the component)
→ renders <YourSection :config="instance.values" />The chain is:
- Page decides which template slot to render (
home,shop,product, ...). - TemplateSections walks the array of section instances for that slot.
- Section registry (
utils/sectionRegistry.ts) returns your component for eachtype. - Your component renders, receiving the merchant-saved
valuesasconfig.
You write code at step 4. Steps 1-3 are framework — already done.
Adding a new file: where to put it
| You're adding... | Goes in... |
|---|---|
| A new section | components/sections/<TypeName>/{index.vue, manifest.json} |
| A shared UI piece used by multiple sections | components/<Name>.vue (e.g. how ProductCard.vue is used by ProductGrid, ProductRelated, etc.) |
| A new page route | pages/<route>.vue — but check first; new public routes change the SEO surface and usually need product approval |
| A new composable | composables/use<Name>.ts — only if it's genuinely reusable; one-off helpers go inline in the section |
| A new pure helper | utils/<name>.ts |
| A new TypeScript type used by sections | types/section.ts if section-related, otherwise alongside the file that uses it |
A note on "core" files
You may have seen the legacy docs talk about @storeino/template-core-v2 — a separately-installed package containing plugins and utils. Souk-theme does NOT use that package. Plugins live directly in plugins/, helpers in utils/. Easier to read, easier to debug, no version-skew between theme and the shared package. Ignore any reference to template-core-v2.