storefront-widget
SKILL.md
Scripttag Development (Storefront Widget)
Overview
The scripttag package contains customer-facing storefront widgets injected into merchant stores. Performance is CRITICAL - every KB and millisecond impacts merchant store speed and conversion rates.
Architecture
Tech Stack
| Technology | Purpose | Why |
|---|---|---|
| Preact | UI library | 3KB vs React's 40KB+ |
| preact-lazy | Lazy loading | Lightweight lazy loader |
| SCSS | Styling | Scoped styles, minimal footprint |
| Rspack | Bundler | 10x faster than webpack |
Styling: Always use custom SCSS/CSS. Avoid UI libraries - they add unnecessary bundle size.
Directory Structure
packages/scripttag/
├── src/ # Main widget entry
│ ├── index.js # Main entry point
│ ├── loader.js # Minimal loader script
│ ├── components/ # Shared components
│ ├── managers/ # API, Display managers
│ ├── helpers/ # Utility functions
│ └── styles/ # Global styles
├── [feature-name]/ # Feature-specific modules
│ ├── index.js # Feature entry point
│ ├── components/ # Feature components
│ └── helpers/ # Feature helpers
└── rspack.config.js # Build configuration
Performance Rules (CRITICAL)
1. Minimal Loader Pattern
// loader.js - Keep as small as possible (~2KB)
function loadScript() {
const script = document.createElement('script');
script.async = true;
script.src = `${CDN_URL}/main.min.js`;
document.head.appendChild(script);
}
// Load after page ready (non-blocking)
if (document.readyState === 'complete') {
setTimeout(loadScript, 1);
} else {
window.addEventListener('load', loadScript, false);
}
2. Lazy Loading Components
import lazy from 'preact-lazy';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
3. Tree Shaking
// BAD: Import entire library
import * as utils from '@avada/utils';
// GOOD: Import only what you need
import {isEmpty} from '@avada/utils/lib/isEmpty';
// BAD: Barrel imports
import {formatDate, formatCurrency} from '../helpers';
// GOOD: Direct path imports
import formatDate from '../helpers/formatDate';
import formatCurrency from '../helpers/formatCurrency';
4. Bundle Size Limits
| Component | Target Size |
|---|---|
| Loader script | < 3KB gzipped |
| Main bundle | < 50KB gzipped |
| Feature chunk | < 30KB gzipped |
| Initial load total | < 60KB gzipped |
Preact Patterns
Use Preact Instead of React
// Use preact directly
import {render} from 'preact';
import {useState, useEffect} from 'preact/hooks';
// Rspack aliases handle React compat:
// 'react' -> 'preact/compat'
// 'react-dom' -> 'preact/compat'
Functional Components with Hooks
import {useState, useEffect, useMemo, useCallback} from 'preact/hooks';
function Widget() {
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
return data ? <Display data={data} /> : null;
}
Styling (Recommended Approach)
Custom SCSS (Preferred)
// Lightweight custom styles with BEM
.widget {
&__button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background: var(--primary-color);
color: white;
cursor: pointer;
&:hover {
opacity: 0.9;
}
&--secondary {
background: transparent;
border: 1px solid var(--primary-color);
color: var(--primary-color);
}
}
}
CSS Variables for Theming
:root {
--primary-color: #{$primaryColor};
--text-color: #{$textColor};
--bg-color: #{$backgroundColor};
}
.card {
background: var(--bg-color);
color: var(--text-color);
}
Window Data Pattern
Storefront widgets receive data via global window object:
const {
shop, // Shop configuration
customer, // Current customer data
settings, // Widget settings
translation, // i18n translations
} = window.APP_DATA || {};
// Always destructure with defaults
const {items = [], config = {}} = settings || {};
Development Commands
# Development with watch
npm run watch
# Production build
npm run build
# Analyze bundle size
npm run build:analyze
# Development build (unminified)
npm run build:dev
Checklist
Before Commit
- No barrel imports (use direct paths)
- Heavy components lazy loaded
- Dynamic imports for conditional features
- Tree-shaking friendly imports
- No console.log in production
- Custom SCSS with BEM naming
- No UI library dependencies
Bundle Size Check
- Run build:analyze
- Loader < 3KB gzipped
- No unexpected large chunks
- No duplicate dependencies
- All imports use direct paths
Performance
- Loads after document ready
- Non-blocking script loading
- Retry logic with backoff
- Performance tracking in place
- No synchronous heavy operations
Weekly Installs
2
Repository
trantuananh-17/…-reviewsFirst Seen
Jan 30, 2026
Security Audits
Installed on
cursor2
mistral-vibe2
qwen-code2
claude-code2
github-copilot2
codex2