v2.2.1
glanceapp/glancev2.2.1Aug 12, 2026by github-actions[bot]
AI Summary
A major EverShop release consolidating React 19 migration with four months of development, introducing a visual page builder, blog module, metafields, multi-language storefront and admin, rebuilt shipping/fulfillment stack, cloud storage, product recommendations, and substantial security and performance improvements.
Key Highlights
- Visual Page Builder with drag-and-drop editing and scheduled rollouts
- Blog module with posts, categories, tags, comments, and storefront pages
- Entity custom fields (metafields) for products, categories, collections, customers, orders, and shop
- Multi-language storefront with independently-translated admin (17 locales)
- React 19 upgrade with per-IP rate limiting and large-catalog performance fixes
Breaking Changes
- Upgraded to React 19 (from React 17) - requires code migration for defaultProps, legacy refs, ReactDOM.render, legacy context, and translation helper usage patterns
- react-toastify removed - must switch to sonner for toasts
- Shipping is now provider-based - old flat zone→method→rate model replaced
- Fulfillment is now multi-shipment - order ↔ shipment changed from 1:1 to one-to-many
- Widget storage refactor - widget table renamed to widget_instance with placement in widget_placement table
- Branding and several config keys moved from config.json to admin settings
- category.include_in_nav dropped - storefront navigation now uses menu widgets
- url_rewrite.language dropped - locale is expressed as URL prefix
- node-cron upgraded 3 → 4 - verify custom schedule expressions
- --skip-minify removed - builds are always minified
- CKEditor packages removed
New Features
- Visual Page Builder (pageBuilder core module) with drafts, publish, and scheduled rollouts
- Blog (blog core module) with posts, categories, tags, comments, storefront pages, and widgets
- Entity custom fields (metafields) with typed definitions and theme integration via <Metafield> component
- Multi-language storefront with runtime translation and independently-translated admin
- Shipping providers & multi-shipment with pluggable providers and carrier/label integration
- Cloud file storage with S3, Azure Blob, and Google Cloud Storage built-in
- Product recommendations including related products, frequently-bought-together, upsell and cart cross-sell
- React 19 with per-IP rate limiting, sitemap.xml + robots.txt, landing pages, automatic URL redirects
Full Release Notes
The largest EverShop release since 2.0. It consolidates the previously drafted-but-unpublished 2.1.3 work (React 19) with four months of development on top of v2.1.2: a visual page builder, a blog module, entity custom fields (metafields), a multi-language storefront with a translated admin, a rebuilt shipping and fulfillment stack, built-in cloud file storage, product recommendations, and a substantial security and performance pass.
31 database migrations across 10 modules run automatically on first start. Please read **Breaking Changes** and **Upgrade Notes** before upgrading. This release also contains fixes for several security vulnerabilities — upgrading promptly is recommended.
## Highlights
* **Visual Page Builder** — drag-and-drop storefront editing with drafts, publish, and scheduled rollouts.
* **Blog** — a full blog module: posts, categories, tags, comments, storefront pages and widgets.
* **Metafields** — typed custom fields on products, categories, collections, customers, orders, and the shop itself, with theme provisioning.
* **Admin translation** — runtime-translated storefront and independently-translated admin (17 locales bundled).
* **Shipping providers & multi-shipment** — pluggable shipping providers, first-class shipments with per-shipment status, carrier/label integration, package management.
* **Cloud file storage** — S3, Azure Blob, and Google Cloud Storage built in, configured from the admin.
* **Product recommendations** — related products, frequently-bought-together, upsell and cart cross-sell shelves.
* **React 19**, per-IP rate limiting, sitemap.xml + robots.txt, landing pages, automatic URL redirects, and large-catalog performance fixes.
## Breaking Changes
* **Upgraded to React 19** (from React 17) (#863). The framework runs on React 19, and because EverShop resolves React through a single hoisted copy (webpack alias), every extension and theme now runs on React 19 as well. Update React-17-era code:
- `defaultProps` on function components is removed — use ES default parameters (`function C({ x = 1 })`).
- String refs, `findDOMNode`, legacy `ReactDOM.render`/`ReactDOM.hydrate`, and legacy context are removed — use callback/`useRef` refs, `createRoot`/`hydrateRoot`, and `createContext`.
- Do not call the translation helper `_()` at module scope (top-level `const` arrays/objects): it freezes the translation at import time and causes an SSR/client hydration mismatch under React 19. Call `_()` inside the component (render time).
- Read client-only state (e.g. `window.location`) in a `useEffect`, not in a `useState` initializer, so the first client render matches the server.
- See `specifications/react-19-upgrade.md` for the full audit and migration detail.
* **`react-toastify` removed — toasts now use `sonner`.** Extensions importing `react-toastify` must switch: `import { toast } from 'sonner'` (the `toast.*` call surface is close to drop-in) and replace `<ToastContainer/>` with sonner's `<Toaster/>`.
* **Shipping is now provider-based** (#929). The old flat zone → method → rate model was replaced by *shipping providers*. Existing zones, methods, and rates are migrated automatically into the built-in **Core provider** (`core_shipping_method`, `core_shipping_method_rate`, `shipping_zone_provider` tables); the legacy tables/columns are dropped at the end of the migration chain. Custom code that queried the legacy shipping tables, or extensions that added shipping methods directly, must move to the provider registry (which also supports live-quoted rates with per-provider timeouts).
* **Fulfillment is now multi-shipment.** `order ↔ shipment` went from 1:1 to one-to-many: each shipment carries its own item assignments and its own status, and `order.shipment_status` is an item-based rollup over physical items. The pre-shipped state was dropped — legacy `pending`/`processing` shipment rows are collapsed to `shipped` and order rollups are backfilled by migration. Consumers of the shipment REST/GraphQL shapes must adapt.
* **Widget storage refactor.** The `widget` table was renamed to `widget_instance`, and placement moved to a `widget_placement` table with one row per (widget instance, route, area). Widget instances are now scoped to the theme they were created under. Any direct SQL or custom tooling against the old `widget` table breaks; the admin UI, REST, and GraphQL surfaces are updated.
* **Branding and several config keys moved from `config.json` to admin settings.** The logo, favicon, social banner, and Google Analytics 4 ID are now managed under Store Setting → Branding; currency, display timezone, weight and dimension units under Store Setting; catalog, pricing, and tax options under their own settings pages; guest checkout under checkout settings. Legacy config values are still honored as fallbacks — **except `themeConfig.logo`, which was removed entirely: re-upload your logo in the admin after upgrading.**
* **`category.include_in_nav` dropped.** Storefront navigation is built with the menu widgets now (including the new footer menu widget), not by flagging categories. Rebuild your navigation with the menu widget if you relied on this column; GraphQL no longer exposes `includeInNav`.
* **`url_rewrite.language` dropped.** URLs are locale-agnostic: locale is expressed as a URL prefix (`/de/<slug>`), and the slug is shared across locales.
* **`node-cron` upgraded 3 → 4.** Verify custom `registerJob` schedule expressions against node-cron v4 semantics.
* **`--skip-minify` removed** (and the `build-fast` npm script). Builds are always minified; overall build time was optimized instead (see Performance).
* **Removed the unused CKEditor packages** (`@ckeditor/ckeditor5-build-classic`, `@ckeditor/ckeditor5-react`).
## New Features
### Visual Page Builder (new `pageBuilder` core module)
A drag-and-drop visual editor for the storefront at `/admin/page-builder`:
* Edit any storefront route (plus CMS pages and landing pages) by composing widgets into the theme's areas, with layout-aware drag/drop and moves.
* **Draft → publish workflow**: changes accumulate in a per-admin, per-theme draft changeset with per-widget auto-save; publish immediately or **schedule a rollout** — rollout plans remain editable and cancelable until they run.
* Inline editing on the canvas: text and images can be edited in place; the image picker supports cloud storage.
* Layers panel, a "Globals" view for site-wide areas, a session/page picker (Homepage first), per-widget styling controls, and link fields that resolve products/categories/CMS pages/blog posts through a unified link resolver.
* Widget admin redesign to match: theme-scoped widget instances and a per-(route, area) placements model.
* Hardened in a dedicated security pass and covered by an e2e test suite.
### Blog (new `blog` core module) (#285)
* Posts, categories, and tags with per-post SEO descriptions; comments and reactions.
* Storefront blog pages plus blog widgets for the page builder; Blog Home is available in the link picker with proper thumbnails.
### Entity custom fields — metafields
* Typed metafield definitions (text, long text, rich text via the block editor, number, boolean, date, select, JSON/group, and list variants) attachable to **products, categories, collections, customers, orders, and the shop**.
* Values stored per entity (JSONB `meta_data`), validated with AJV on write; admin editing cards on the relevant edit pages; audience-gated GraphQL exposure (customer-visible vs admin-only).
* **Theme integration**: a theme can declare metafield definitions in `theme.json` — they are provisioned at theme activation and on boot (with attribution and conflict reporting) — and render values with the new storefront `<Metafield>` component.
* The footer copyright line is now a shop metafield, editable from the page builder.
### Multi-language storefront & translated admin (#311)
* **Runtime translation** — no rebuild needed. Per-locale CSV dictionaries live in the project's `translations/` folder; English is the source language.
* Enabled storefront languages and the default language are admin settings. Non-default locales get URL prefixes (`/de/...`) with canonical handling; REST APIs take an `X-Locale` header.
* The **admin panel has its own language**, independent of the storefront.
* Translations are bundled for 17 locales (de, el, es, fa, fr, hu, it, mn, nb, ne, nl, pt, ru, sr, ta, vi, zh).
### Landing pages, root-level CMS URLs & automatic URL redirects
* **Landing pages**: standalone marketing pages whose body is built entirely in the page builder, served at root-level friendly URLs, with an admin grid, duplication, status/scheduling, and SEO fields. Landing pages win URL collisions deterministically.
* **CMS pages moved to root-level URLs** (`/<url_key>`); old `/page/<url_key>` URLs permanently redirect (301). Existing pages are backfilled by migration.
* **Automatic URL redirects**: renaming a product/category/CMS/landing-page slug now captures a redirect from the old path (302 via a new `url_redirect` table), with redirect-chain collapse and periodic cleanup — old links and bookmarks keep working.
### Sitemap & robots.txt (#506)
* Auto-generated `/sitemap.xml` (a sitemap index plus per-content-type children) covering products, categories, CMS pages, and landing pages, with a **collector registry** so extensions can add their own URL sources.
* Multi-language stores get one entry per enabled locale with self-referential `hreflang` + `x-default` alternates.
* Regenerated by a cron every 30 minutes with cheap change-detection (skips writing when nothing changed); served statically.
* Dynamic `robots.txt` with an absolute `Sitemap:` line — overridable via a setting or by shipping a physical `public/robots.txt`.
### Shipping providers, multi-shipment & carrier integration (#929)
* **Provider abstraction**: shipping rates come from registered providers; the built-in Core provider reproduces the classic zone/method/rate setup (flat, percentage, price/weight-based rates, API-calculated rates). Extension providers can quote live rates with configurable timeouts.
* **Multi-shipment**: ship an order in several packages — each shipment has its own items, status, tracking, and (optionally) purchased label (`label_url`/format persisted). Order-level shipment status rolls up from items; digital items are excluded.
* **Carrier registry**: register carriers that create labels, generate tracking URLs, and push status updates back into EverShop; per-method default carrier and service code pre-select in the ship dialog; carrier tracking URLs from aggregators (Shippo/EasyPost/ShipStation-style) are persisted.
* **Package management**: admin-managed parcel sizes, product → package assignment, and dimension/weight snapshots that flow `cart_item` → `order_item` → carrier requests.
### Cloud file storage: S3, Azure Blob & Google Cloud Storage
* The three providers are now built into core (previously separate extensions), selected and configured at runtime from a new **System Setting** page — no restart needed; credentials can also come from env/config, and secrets are masked in the admin.
* Fixes doc-verified defects of the old extensions along the way: regional S3 endpoints (plus custom endpoint / path-style for R2 and MinIO), correct Content-Type on upload, listing beyond 1,000 keys, Azure public-access handling, and URL encoding.
* The storage host is automatically allowed for the `/images` optimizer; deletes are idempotent across providers.
### Product recommendations
* **Related Products**: rule-based (same category / same collection / same attribute values, with price band, priority ordering, and manual picks), configurable globally, per category, and per product.
* **Frequently Bought Together**: powered by co-purchase statistics (association confidence + lift with configurable thresholds), rebuilt nightly and on demand from order history. Also reaches the **cart page**: a widget aggregates co-purchase candidates across all cart items (per-pair gating, strongest-affinity ranking, in-cart items and their variants excluded), exposed as `Cart.crossSellProducts`.
* **Upsell shelf**: derived automatically from the related-products rules restricted to pricier products, exposed as `Product.upsellProducts` — nothing extra to configure.
* Three new page-builder widgets render the shelves on product pages with full status/stock/visibility gating and variant-group awareness; admin cards on product and category editing (modes, manual picks, computed-candidate previews); new REST endpoints for manual links and stats recompute; new GraphQL fields `Product.relatedProducts` / `Product.crossSellProducts`.
### Checkout
* **Zero-total orders** (100% discounts, free products) check out through an automatic "No payment required" method instead of erroring (#996).
* **Guest checkout toggle** — an admin setting; when off, checkout requires login (with redirect back). Defaults to allowed; the legacy `checkout.allowGuestCheckout` config is honored as fallback.
* **Mobile checkout**: the order summary now comes first as a sticky, collapsible bar; shipping/payment/billing option cards are clickable across their whole surface.
* Fixed: shipping note not showing on checkout (#955); billing country missing/restricted for virtual products (#934).
### Storefront theme refresh & UX
* The default frontStore theme was re-skinned, with aligned category-page filters and product counts, a constrained footer, and grid-safe no-image placeholders.
* **Accessibility pass**: product-card Add-to-Cart is reachable by keyboard and touch (not hover-only), field error messages are wired to inputs via `aria-describedby`.
* Unavailable variant options are disabled in the storefront selector (#964); storefront images are sized from the configured aspect ratio (less layout shift); slideshow slides size to their images with real mobile dimensions.
* Live **SEO snippet preview** on product and category editors.
* **Product duplication** (#276): duplicate from the product grid into a prefilled creation form (suffixed sku/url_key/name, collections copied, `product_duplicated` event).
* New **footer menu widget**; refined collection/blog/mosaic widget layout settings.
### Platform & operations
* **Per-IP rate limiting** built in (#1001): pages ~300 req/min, APIs 120 req/min, and login/registration/password-reset 8 attempts per 15 minutes; static assets exempt. Honors `TRUST_PROXY_HOPS` for correct client IPs behind proxies (default: 1 hop).
* **`EVERSHOP_HOME_URL`** environment override for the public base URL, validated at boot (#960).
* **Theme content tooling**: new `theme:status`, `theme:uninstall`, and `theme:export-content` commands; themes can ship installable content (`theme.json`) and serve their own `public/` assets (#830, #845 — thanks @starry-osean).
## Security
Upgrading is recommended — this release fixes:
* Unauthenticated SSRF, unauthenticated IDOR on customer endpoints, and stored XSS (CWE-918, CWE-639, CWE-79) (#928).
* Unauthenticated account takeover via missing authorization on the customer update endpoint (#952).
* A page-builder security hardening pass (URL/content validation in the editor pipeline).
* Brute-force / credential-stuffing exposure on auth endpoints (covered by the new rate limiter).
* All high-severity dependency alerts cleared (#937).
## Performance
* **Large-catalog fixes**, found by load-testing a 500k-product catalog (#1004): indexed `url_rewrite.request_path` (every storefront request resolves URLs against it), fixed a keyword-search query shape that bypassed the GIN index, and removed an O(n²) id-list query-building pattern that also hit the 65,535-parameter wire limit.
* **Build time** significantly reduced (#967–#969); minification is always on.
* SSR render errors now return a 500 instead of hanging the request; GraphQL field errors return partial data instead of failing the page.
* Session-store and boot-path cleanups; the setting cache is warmed at boot for synchronous hot paths (pricing formatter, email helpers).
## Bug Fixes
* Built-in `<Form>` buttons no longer double-submit (every built-in form used to POST twice).
* Google Analytics now loads in production builds.
* Fresh installs no longer log a red `relation "setting" does not exist` error on first boot against an empty database.
* Migration runner sorts versions by semver correctly (`1.0.10` no longer runs before `1.0.2`) (#961).
* Widget types may share component files (settingComponent/previewComponent) — fixed an AreaLoader identifier bug that broke the admin bundle when two types pointed at one file.
* `cms:page` widget links resolve through `url_rewrite` (they rendered dead links before).
* Cloud-storage image URLs keep their scheme intact in the page-builder image picker.
* Empty settings are no longer saved as the string `"null"`.
* Shipping zone dialog sizing fixed; province selection uses a searchable select.
* Setup no longer crashes with `ValidationError: Progress Plugin has been initialized using an options object` (#932); GitHub Actions build no longer fails on the SWC native module.
* Dev server boot is hardened against compile failures and file-watcher races.
* Variant option filter guards undefined values; metafield single-field setters fixed (#1006).
* Page-builder fixes: publish keeps you on the current route, the session picker no longer pops mid-session, layers show the right widgets on landing pages, draft-aware widget lookups, drops into empty areas.
## Developer Experience
* New public import paths: `@evershop/evershop/lib/urn`, `@evershop/evershop/lib/metafield`, `@evershop/evershop/lib/widget/linkResolver`, and `@evershop/evershop/base/services/sitemap` (register your own sitemap collectors).
* Two new core modules (`blog`, `pageBuilder`) demonstrate current module conventions end-to-end; the page builder ships with an e2e suite.
* Runtime translation APIs and the locale context are available to extensions; admin strings are translatable.
## Dependencies
* Added: `@aws-sdk/client-s3`, `@azure/storage-blob`, `@google-cloud/storage`, `express-rate-limit`, `sonner`, `undici`, `fast-json-stable-stringify`, `@evershop/editorjs-product-list`.
* Upgraded: `react`/`react-dom` 17 → 19, `node-cron` 3 → 4, `webpackbar` 5 → 7, `axios` 1.18, `multer` 2.2.
* Removed: `react-toastify`, `@ckeditor/ckeditor5-build-classic`, `@ckeditor/ckeditor5-react`.
## Upgrade Notes
1. **Back up your database.** This release runs 31 migrations, several of which transform data (shipping zones → Core provider, shipment status collapse, widget table rename) and drop legacy columns/tables.
2. Update `@evershop/evershop`, reinstall dependencies, run `npm run build`. Migrations apply automatically on first start.
3. **Re-upload your logo** in admin → Store Setting → Branding (`themeConfig.logo` is gone). Favicon, social banner, and GA4 also live there now.
4. If your navigation relied on `include_in_nav`, rebuild it with the menu widgets.
5. Review your shipping setup under the new provider-based settings UI — data is migrated automatically, but verify rates.
6. For custom themes/extensions: apply the React 19 notes above, switch `react-toastify` imports to `sonner`, and update anything that touched the `widget` table or legacy shipping tables directly.
7. Custom cron jobs: verify schedules against node-cron v4.
## Credits
Thanks to everyone who contributed to this release: @treoden, @starry-osean (theme public assets, #845), Sang.Tra.