vite-plugin-shopify-theme-islands
Version:
Vite plugin for island architecture in Shopify themes
256 lines (187 loc) • 6.34 kB
Markdown
name: setup
description: >
Getting-started journey and plugin configuration. Covers the full path from
install to first working island. shopifyThemeIslands() options: directories
(string | string[]), debug, directives deep-merge (visible, idle, media,
defer, interaction, custom), retry (retries, delay with exponential
backoff), and directiveTimeout for hung custom directives. Load when setting
up the plugin, configuring island scan directories, or enabling retry /
directive timeout.
type: core
library: vite-plugin-shopify-theme-islands
library_version: "1.2.2"
sources:
- Rees1993/vite-plugin-shopify-theme-islands:src/index.ts
- Rees1993/vite-plugin-shopify-theme-islands:src/contract.ts
- Rees1993/vite-plugin-shopify-theme-islands:src/options.ts
- Rees1993/vite-plugin-shopify-theme-islands:src/config-policy.ts
## Setup
This plugin is framework-agnostic but designed for Shopify themes. Most Shopify
projects also use
[vite-plugin-shopify](https://github.com/barrel/vite-plugin-shopify) to handle
Shopify-specific asset serving — if the project uses it, add this plugin
alongside it in the existing `plugins` array.
### 1. Add the plugin to `vite.config.ts`
```ts
// vite.config.ts
import { defineConfig } from "vite";
import shopifyThemeIslands from "vite-plugin-shopify-theme-islands";
export default defineConfig({
plugins: [shopifyThemeIslands()],
});
```
All options are optional. The default islands directory is `/frontend/js/islands/`.
### 2. Import the virtual module in the theme JS entry point
```ts
// frontend/js/theme.ts
import "vite-plugin-shopify-theme-islands/revive";
```
This activates the runtime — islands are never loaded without this import.
### 3. Add directives to Liquid templates
```html
<!-- sections/product.liquid -->
<product-form client:visible></product-form>
```
That's a working setup. Islands in `/frontend/js/islands/` matching the tag
name are loaded lazily when the directive condition is met.
## Core Patterns
### Configure multiple island directories
```ts
shopifyThemeIslands({
directories: ["/frontend/js/islands/", "/frontend/js/components/"],
});
```
### Override built-in directive defaults
```ts
shopifyThemeIslands({
directives: {
visible: { rootMargin: "0px", threshold: 0.5 },
idle: { timeout: 2000 },
defer: { delay: 5000 },
interaction: { events: ["mouseenter"] },
},
});
```
Per-directive options are deep-merged — overriding `visible.rootMargin` preserves `visible.threshold` at its default of `0`.
### Enable automatic retry with exponential backoff
```ts
shopifyThemeIslands({
retry: { retries: 3, delay: 1000 },
});
```
`retries` is the number of attempts after the first failure. `delay` is the base ms — each subsequent retry doubles it (1000ms → 2000ms → 4000ms).
### Guard against hung custom directives
```ts
shopifyThemeIslands({
directiveTimeout: 5000,
});
```
When a custom directive never calls `load()`, the runtime normally waits forever. `directiveTimeout` turns that into an `islands:error` event and abandons the activation attempt after the configured number of milliseconds.
### Enable console debug output
```ts
shopifyThemeIslands({ debug: true });
```
Logs discovered islands, active directives per element, and load/error events at startup.
## Common Mistakes
### CRITICAL Virtual module not imported — islands never activate
Wrong:
```ts
// vite.config.ts — plugin configured but virtual module never imported
shopifyThemeIslands({ directories: ["/frontend/js/islands/"] });
```
Correct:
```ts
// frontend/js/theme.ts
import "vite-plugin-shopify-theme-islands/revive";
```
The plugin generates the virtual module but has no effect until it is imported in the browser entry point. Islands are silently never activated.
Source: src/index.ts — VIRTUAL_ID / RESOLVED_ID
### HIGH Agent hardcodes default values — unnecessary noise
Wrong:
```ts
shopifyThemeIslands({
directories: ["/frontend/js/islands/"],
debug: false,
directives: {
visible: { attribute: "client:visible", rootMargin: "200px", threshold: 0 },
idle: { attribute: "client:idle", timeout: 500 },
media: { attribute: "client:media" },
defer: { attribute: "client:defer", delay: 3000 },
interaction: { attribute: "client:interaction", events: ["mouseenter", "touchstart", "focusin"] },
},
});
```
Correct:
```ts
shopifyThemeIslands();
```
All options are optional and default to sensible values. Only include options that differ from the defaults.
### HIGH Agent overwrites existing `vite.config.ts` instead of appending
Before adding the plugin, read the existing `vite.config.ts`. Projects commonly
already have `vite-plugin-shopify` or other plugins — the island plugin must be
added to the existing `plugins` array, not replace it.
Wrong:
```ts
// Replaces existing plugins
export default defineConfig({
plugins: [shopifyThemeIslands()],
});
```
Correct:
```ts
// Appends to existing plugins
export default defineConfig({
plugins: [
shopify(), // pre-existing plugin preserved
shopifyThemeIslands(),
],
});
```
### HIGH `retry` nested inside `directives` — no retries happen
Wrong:
```ts
shopifyThemeIslands({
directives: {
retry: { retries: 2 }, // ← wrong nesting
},
});
```
Correct:
```ts
shopifyThemeIslands({
retry: { retries: 2 }, // ← top-level option
});
```
`directives` accepts only `visible`, `idle`, `media`, `defer`, `interaction`, and `custom`. `retry` at `directives.retry` is silently ignored.
Source: src/options.ts — ShopifyThemeIslandsOptions
### HIGH Wrong key name for retry count
Wrong:
```ts
shopifyThemeIslands({ retry: { count: 3 } });
shopifyThemeIslands({ retry: { attempts: 3 } });
```
Correct:
```ts
shopifyThemeIslands({ retry: { retries: 3 } });
```
Unknown keys are silently ignored. The correct field is `retries`.
Source: src/contract.ts — RetryConfig
### HIGH `directiveTimeout` nested inside `directives` — timeout guard never applies
Wrong:
```ts
shopifyThemeIslands({
directives: {
directiveTimeout: 5000,
},
});
```
Correct:
```ts
shopifyThemeIslands({
directiveTimeout: 5000,
});
```
`directiveTimeout` is a top-level plugin option, not part of the per-directive config object.
Source: src/options.ts — ShopifyThemeIslandsOptions