2026 08 header de

Laioutr Release News - August 2026

Der August stand im Zeichen der Messbarkeit. Mit dem neuen consent-bewussten Analytics-System bekommt die Plattform ein typisiertes Event-Modell: useAnalytics().track(Token, payload) gibt ein Ereignis ab, dessen Token sein eigenes Payload-Schema mitbringt, und die Auslieferung an jeden einzelnen Empfänger wird erst zum Zeitpunkt der Zustellung gegen die Einwilligung des Besuchers geprüft. Laioutr UI hat die Commerce-Ereignisse einer Storefront direkt darauf verdrahtet — Produktlisten, Produktseiten, Warenkorb und Suche melden ohne jede Konfiguration. Parallel dazu wurde Einwilligung von Cookie-Kategorien auf Verarbeitungszwecke umgestellt, und der ConsentAdapter-Vertrag ist auf vier Mitglieder geschrumpft.

Der zweite Schwerpunkt ist Geschwindigkeit. Orchestr löst die Queries einer Anfrage, die Links einer Query und die Component-Resolver eines Entity-Typs jetzt nebenläufig auf: Der Server-Render einer Storefront-Startseite fiel von rund 724 ms auf rund 322 ms. Dazu kommen um etwa 45 % kürzere Cache-Schlüssel, eine deklarative Cache-Konfiguration, die Orchestr den Schlüssel vollständig selbst bauen lässt, und mehrere Korrekturen an Schlüsseln, die zuvor Markt, Offset oder Sprache nicht enthielten. In Laioutr UI hydrieren Slider und Videos erst in Nähe des Viewports, und Web-Fonts werden nur noch als woff2 ausgeliefert.

Drittens: Beobachtbarkeit. Frontend Core und Orchestr exportieren OpenTelemetry-Traces der serverseitigen Arbeit — Queries, Links und Component-Resolver als verschachtelte Spans, jeder Cache-Zugriff mit einem Verdikt. Und viertens Betrieb: Märkte haben jetzt einen Status (Entwurf oder Aktiv), ein Projekt kann seinen Standardmarkt explizit benennen, und Studio hält den geöffneten Stand — Seite, Auswahl, Markt, Sprache, Vorschau — in der URL.

Frontend Core v0.38.2 – v0.47.2

v0.38.2 wurde am 31. Juli veröffentlicht, aber erst im August dokumentiert und lief deshalb nicht im Juli-Post mit. Sie trägt einen Breaking Change und ist hier aufgeführt.

Highlights

  • Consent-bewusstes Analytics-System: useAnalytics() liefert { track, register, unregister }. track(Token, payload) gibt ein typisiertes Ereignis ab, dessen Token sein eigenes Payload-Schema trägt; @laioutr-core/core-types/analytics stellt die Token-Factories und die plattformeigenen web/*-Ereignisse bereit, @laioutr-core/canonical-types/analytics das ecommerce/*-Commerce-Vokabular. Empfänger werden mit defineAnalyticsDestination registriert und deklarieren ihren Bedarf als consent: { purposes: ['analytics'] } oder über purposeSets als ODER-von-UND-Verknüpfung. Die Einwilligung wird pro Empfänger zum Zeitpunkt der Zustellung ausgewertet, nie bei der Abgabe — eine Abgabe fächert sich also genau auf die Ziele auf, die der Besucher erlaubt hat. Ereignisse, die vor der Antwort des Besuchers entstehen, werden gehalten und nach einer Erteilung in ihrer Reihenfolge nachgespielt. Ein Ziel kann alternativ onDenied deklarieren und abgelehnte Ereignisse mit dem Einwilligungszustand entgegennehmen, statt zu verstummen; ein solches Ereignis gilt als zugestellt und wird bei einer späteren Erteilung nicht erneut gespielt, und ein Widerruf lässt dieses Ziel weiterlaufen statt es abzubauen.
  • Payloads tragen Orchestr-Entitäten direkt: track(AddToCart, { products: [{ entity: product, quantity: 2 }] }) — jede Entität wird zur Abgabezeit auf eine flache Wire-Momentaufnahme projiziert, ausgewählt über ihren entityType. Trägt eine Entität einen Slug auf ihrer base-Komponente, bekommt sie zusätzlich eine absolute url auf dem Produktions-Host des Markts. @laioutr-core/core-types/orchestr erhält dafür getEntityComponent(entity, Token) und getLinkedEntities(entity, LinkToken). Jedes Payload-Schema führt einen optionalen customFields-Beutel für seiten- oder anbieterspezifische Daten; Schlüssel dort gehören mit einem Namensraum versehen ('acme:productLine').
  • Erfassung und Erweiterung: Neu sind die Direktiven v-track-click und v-track-impression sowie useTrackImpression, useTrackScrollDepth und useTrackVideoProgress. Ein Umgebungskontext aus Seite, Markt, Sitzung, Einwilligung und Experiment hängt sich an jedes Ereignis; useAnalyticsContexts() ergänzt einen eigenen Provider oder überschreibt einen der plattformeigenen. Drei synchrone Nuxt-Hooks erweitern die Pipeline: :emit als Veto, :enrich zur Anreicherung und ein :project-Filter pro Entität, wobei augmentProjection einen :project-Handler typisiert.
  • Serverseite und Identität: Der Browser schickt Stapel an POST /api/frontend/signals; der Pfad ist über analyticsIngestPath in der öffentlichen Runtime-Config des Moduls überschreibbar, was die Server-Route mitverschiebt. Jedes Ereignis eines Stapels wird für sich beurteilt — ein fehlerhaftes oder zu großes Ereignis kostet nie den Stapel, in dem es mitfuhr. subscribeToAnalytics registriert einen Empfänger, der nicht im Browser laufen darf; Handler erhalten sentAt und receivedAt neben dem Ereignis. Besucher- und Sitzungs-Cookies entstehen ausschließlich unter dem Zweck analytics und werden auf die registrierbare Domain des Markts gesetzt, sodass eine Identität einen Sprung zwischen Subdomains übersteht; auf Plattform-Hostnamen bleiben sie host-gebunden. Innerhalb der Studio-Vorschau werden sie cross-site und partitioniert geschrieben. Ein Widerruf löscht beide, und eine späte erneute Erteilung erzeugt frische Tokens, ohne Aktivität rückwirkend zusammenzufügen.
  • Einwilligung als Verarbeitungszwecke (Breaking): Ein ConsentAdapter meldet jetzt Partial<ConsentState> über necessary, functional, analytics, advertising und personalization und verantwortet die Abbildung aus dem Vokabular seines eigenen Anbieters. Ein Zweck, der in einer Meldung fehlt, gilt als abgelehnt; ein Adapter erteilt einen Zweck, wenn irgendeiner seiner eigenen, der darauf abbildet, erteilt ist. Ein Besucher, der Messung erlaubt und Personalisierung abgelehnt hat, wird Google Consent Mode jetzt als genau das gemeldet — zuvor als Erlaubnis für beide Werbe-Zwecke.
  • Neuer ConsentAdapter-Vertrag (Breaking): Ein Adapter besteht aus vier Mitgliedern — name, setup, openConsentUi und optional hasDecision — und wird mit setAdapter installiert. init, getConsentState, onConsentChange, destroy und isActive fallen zu setup(report) zusammen: das Verdikt des Besuchers sofort und danach bei jeder Änderung melden, optional eine Aufräumfunktion zurückgeben. setup läuft synchron im installierenden Plugin, sodass useHead und useCookie verfügbar sind. Storefront-seitig ersetzt ein openConsentUi beide früheren Overlay-Aufrufe, und adapterName ersetzt activeAdapter.
  • hasDecision() unterscheidet Ablehnung von Schweigen: useConsentStore() legt offen, ob der Besucher den Einwilligungsdialog überhaupt beantwortet hat. Ein Zustand „abgelehnt" ist andernfalls nicht von „nie gefragt" zu unterscheiden — was überall dort zählt, wo dieser Zustand an einen Dritten weitergegeben wird, der seine eigene regionale Vorgabe anwendet. Die Cookiebot- und CCM19-Apps implementieren es und melden eine gespeicherte Ablehnung als Entscheidung statt als Schweigen. Ab 0.42.0 gibt hasDecision() boolean | undefined zurück: undefined heißt, es existiert überhaupt kein Entscheidungssignal — kein Adapter, oder einer, der es nicht sagen kann.
  • Jeder Fehlschlag einer Link-Auflösung hat einen Namen: linkResolver.resolve() gibt weiterhin einen seiteninternen Rückfall zurück statt zu werfen, aber dieser Rückfall hat eine Form — #<code>?<detail> — aus einer geschlossenen Menge, und jeder trägt mit, was den betroffenen Link identifiziert (#unknown-route?pageId=pdp, #missing-required-params?params=brand). Neu ist linkResolver.resolveOrThrow(link, options?) für Aufrufer, die auf den Fehlschlag reagieren können — ein Analytics-Projektor, ein Sitemap-Writer —, das einen LinkResolutionError mit code und details wirft. Mit { withOrigin: true } kommt eine absolute URL statt eines absoluten Pfads zurück; der Origin stammt aus der Domain des Markts, nie aus location, sodass eine in der Studio-Vorschau oder auf einem Dev-Host aufgelöste Adresse trotzdem die Seite benennt, auf der ein Besucher landen würde.
  • Märkte im Status Entwurf, plus benannter Standardmarkt: Ein Markt auf draft liefert weiterhin seinen eigenen Host aus, damit er vor dem Start geprüft werden kann, ist aber aus hreflang-Alternates, og:locale:alternate, x-default und Markt-Umschaltern ausgenommen und seine Seiten werden mit noindex, nofollow ausgeliefert. switchMarketUrl gibt für ein Entwurfs-Ziel '#market-not-active' zurück. Der Status wird über RenderMarket.isLinkable und isIndexable gelesen statt über das status-Mitglied. RcProject.defaultMarketId ersetzt die implizite Regel „erster Markt in der Konfiguration" für x-default, den primären Route-Pfad, den Rückfall bei unbekanntem Host und nuxt-i18ns defaultLocale — welcher Markt das war, hing zuvor an der Reihenfolge, in der Cockpit sie zurückgab. Bleibt das Feld ungesetzt, gilt das bisherige Verhalten.
  • OpenTelemetry-Tracing: Eine Storefront, die OTEL_EXPORTER_OTLP_ENDPOINT zur Build-Zeit setzt, exportiert Traces ihrer serverseitigen Arbeit über die übliche OTEL_*-Konfiguration; ohne diese Variable wird nichts installiert. Queries, Links und Component-Resolver werden je zu einem Span, verschachtelt unter dem Nitro-Request-Span, und ein Upstream-API-Aufruf während eines Resolvers hängt unter diesem Resolver — ein Trace schreibt Upstream-Zeit also der Arbeit zu, die sie verursacht hat. Attribute sind Zähler und Namen, nach denen ein Backend gruppieren kann: wie viele Entitäten ein Resolver angefragt hat, ein Token-Name, die App, aus der ein Handler kommt. Jeder Cache-Zugriff trägt orchestr.cache.verdict (hit, miss, partial, skip, uncacheable), und ein serverseitig gerenderter Request markiert seinen Span mit Seitentyp, Markt-Slug und Sprache, weil eine Produktseite und eine Listenseite dieselbe Wildcard-Route teilen.
  • Tracing auf Vercel ist ausdrücklich einzuschalten (Breaking): Eine dort deployte Storefront schaltete es zunächst automatisch ein (0.45.0), was jede Anfrage einen fehlgeschlagenen Span-Export und zwei Fehler-Logzeilen kostete, weil Vercel keinen Collector betreibt, den er erreichen könnte. Ab 0.46.0 ist LAIOUTR_OTEL_ENABLED=1 am Projekt zu setzen, um Tracing zu behalten; ein mit OTEL_EXPORTER_OTLP_ENDPOINT konfigurierter Build ist nicht betroffen. Ein getracter Request tritt außerdem dem Trace bei, in dem er ankommt, statt einen zweiten, unverbundenen zu beginnen.
  • laioutr://-Ressourcen-Locator: @laioutr-core/core-types/locator exportiert formatLocator und parseLocator samt der tragenden Typen sowie die Konstante STUDIO_CONTAINER_KINDS. Ein Locator benennt einen Namensraum (studio ist bislang der einzige), einen Container (pageVariant, section, sectionRef, globalSection oder block) über seine ID, einen Pfad aus Objektschlüssel- oder Array-Element-nach-ID-Schritten in dessen Props und optionale Ansichtskoordinaten (locale, market, ref) — etwa laioutr://studio/block/blk_C3/slides[itm_E5]/heading?locale=de. Beide Richtungen beherrschen auch eine relative Form ohne die laioutr://studio/-Basis. parseLocator wirft nie, sondern gibt für jede Eingabe { ok: true, value } | { ok: false, error } zurück.
  • useSectionContext() und useRenderPageContext() sind auto-importiert: Beide Kontexte wurden zur Laufzeit bereits bereitgestellt, waren aber nur über einen tiefen Importpfad erreichbar. Ein Block erhält nie seine eigene ID, weshalb der Sektions-Kontext für ihn die einzige stabile Listen-Identität ist.
  • Analytics-Identität serverseitig lesen: readAnalyticsIdentity(event) gibt die Besucher- und Sitzungs-Tokens zurück, die der Browser auf seinen Ereignissen ohnehin meldet. Eine Server-Route oder ein Connector kann eigene Arbeit damit demselben Besuch zuordnen, ohne eine zweite Identität zu erzeugen. Die Tokens existieren nur unter dem Zweck analytics — ein leeres Ergebnis heißt also, dass der Besucher ihn nicht erteilt hat.
  • Entwicklungs-Schalter unter laioutr.dev: Ein Produktions-Build verwirft das ganze Objekt statt jedes Flag einzeln. Neu ist consentDebug: ein Debug-CMP, das jeden Zweck ohne Nachfrage erteilt. Playgrounds installieren keine App zur Einwilligungsverwaltung, sodass dort nie ein Zweck erteilt wird und die einwilligungsgebundenen Pfade nie laufen. Es meldet eine Entscheidung, die der Besucher nicht getroffen hat, und warnt deshalb bei der Installation. analyticsDebug loggt weiterhin kanonische Ereignisse über das eingebaute Debug-Ziel und greift jetzt nur noch im Dev-Modus.
  • Sitzungs-Cookies überleben die Studio-Vorschau: Warenkorb- und Kundensitzungs-Cookies werden mit SameSite=None; Secure; Partitioned ausgestellt, wenn die Anfrage aus dem Vorschau-Frame kommt, sodass ein im Editor aufgebauter Warenkorb Reloads übersteht. Die Vorschau bekommt ihre eigene Cookie-Partition und bleibt damit von der echten Sitzung im selben Shop im selben Browser getrennt. App-Autoren erhalten die beiden neuen Server-Auto-Imports setManagedCookie und deleteManagedCookie, die diese Richtlinie anwenden — Löschungen müssen zwingend über deleteManagedCookie laufen, weil ein Delete ohne Partitioned den falschen Cookie-Jar adressiert und den Cookie stillschweigend stehen lässt. Secure wird jetzt aus dem Request-Origin abgeleitet statt pro Connector gesetzt.
  • menuTreeAtDepth(items, startLevel): Auto-importiert neben buildMenuTree, baut den Menübaum und steigt dann startLevel Ebenen ab, sodass die Knoten dieser Tiefe zur obersten Ebene werden — gedacht zum Überspringen eines synthetischen Upstream-Wurzelknotens (etwa einer Magento-Kategorie „Root"), damit die erste geschäftlich relevante Ebene oben rendert. 0 (die Vorgabe) lässt den Baum wie geholt.
  • Eine Seite mit einer einzigen Variante holt in einem Request: Seiten-Queries und Varianten-Queries laufen dort jetzt in einer Anfrage statt in zwei seriellen Runden, sodass die Seite früher rendert. Seiten mit mehreren Varianten bleiben unverändert — ein frontend-core:page-renderer:select-page-variant-Handler liest weiterhin die aufgelösten Seiten-Queries, bevor er wählt.

Fehlerbehebungen & Verbesserungen

  • Behoben: Der Reflect-Endpunkt lieferte den Sektions- und Block-Katalog eines vorherigen Deployments aus. Seine gecachte Reflexion wird jetzt nach Build-ID geschlüsselt, sodass ein Redeploy ein Cache-Miss ist statt zu erben, was der letzte Build hinterlassen hat, und zwei Frontends, die ein Redis teilen, überschreiben ihre Einträge nicht mehr gegenseitig. Einträge verfallen nach 12 Stunden.
  • Behoben: Die App-Konfiguration aus laioutrrc.json wurde in jedes App-Modul doppelt gemischt. Sie wurde sowohl nuxt.options[<appName>] zugewiesen als auch an installModule übergeben, sodass Nuxt sie mit sich selbst mischte und jede Array-Option verkettete — eine vierstellige Shopify-sortings-Liste kam als achtstellige an und ließ den Build an einem Schlüsselduplikat scheitern.
  • Behoben: linkResolver.resolve() verwarf die query eines Links. Weil jede Laioutr-Seitenroute localizedPaths trägt, liefen interne Links immer über den Zweig für lokalisierte Pfade — der query ignorierte und vor dem vue-router-Rückfall zurückkehrte, der sie angewandt hätte. Eine Header-Suche landete auf /search ohne ?q=. url- und anchor-Links tragen ihre query nun ebenfalls; sie wird in das bestehende href gemischt (die query des Links gewinnt bei einer Schlüsselkollision) und landet immer vor dem Fragment.
  • Behoben: Der Render-Cache des Seiten-Endpunkts wuchs unbegrenzt und konnte den Speicher einer Storefront erschöpfen. Der Endpunkt merkte sich jede gerenderte Seite je Seite, Markt und Sprache in einer Map, aus der nie verdrängt wurde — und Modul-Scope lebt auf einem serverlosen Host so lange wie die Instanz. Die Map ist jetzt begrenzt.
  • Behoben: Der Consent-Store ist auf die Nuxt-App statt auf das Modul begrenzt, sodass auf dem Server jede Anfrage ihren eigenen bekommt. Zuvor war er modulglobal — auf einem Server also prozessglobal: Jeder gleichzeitige Render teilte einen Store, der vom ersten Request installierte CMP-Adapter bediente mit der damals erfassten Cookie-Ref alle späteren, und jeder weitere Request hängte ihm einen zusätzlichen Consent-Listener an.
  • Behoben: Ein Markt- oder Sprachwechsel im Studio lädt den vorgeschauten Inhalt neu. Die Vorschau renderte weiterhin den Markt, mit dem sie geöffnet wurde — ein Wechsel von Deutsch auf Niederländisch ließ die vorigen Menüs, Produktdaten und Preise stehen, sodass die Vorschau den Inhalt eines Markts unter den Einstellungen eines anderen zeigte. Orchestr-Ergebnisse sind auf Markt und Sprache begrenzt, der Client cachte sie aber unter einem Schlüssel, der keines von beidem trug. Die gecachten Ergebnisse werden bei einer Änderung der Auswahl jetzt verworfen. Das Storefront-Rendering ist nicht betroffen.
  • Behoben: Die kanonischen Seitentypen und die Analytics-Projektoren registrieren sich im Browser. Das Plugin, das sie zog, nutzte einen dynamischen Import mit @vite-ignore und einem Template-Literal-Specifier, sodass Vite den nackten Specifier unangetastet ließ und der Browser ihn nicht auflösen konnte. Der Fehlschlag lief in ein leeres catch, es registrierte sich also nichts, und jede Entität in einem Analytics-Payload fiel auf ihre ID und ihre Adresse zurück. Seitentypen waren nicht betroffen, weil sie auf dem Server auflösen. Das Modul prüft jetzt zur Build-Zeit, ob das Paket installiert ist, und emittiert dann ein Plugin, das es statisch importiert; das Paket bleibt optional.
  • Behoben: Eine Seite führt keine Query mehr aus, die nur noch ein verwaistes Feld bindet. Lässt eine Sektion oder ein Block ein Feld aus ihrem Schema fallen, bleibt der gespeicherte Wert in der Projektkonfiguration — und dieser Wert hielt die Query am Leben, sodass jeder Render Daten holte, die kein Component liest. Ein Header, der auf diesem Weg noch eine Warenkorb-Bindung hielt, kostete einen Upstream-Request auf jeder Seite der Storefront. Eine Query überlebt jetzt so lange, wie irgendein lebendes Feld oder ein SEO-Platzhalter sie referenziert; eine Sektion oder ein Block, den keine Registry kennt, verliert seine Queries ebenfalls. Jeder Wegfall warnt einmal und benennt Component, Feld und Query-Token.
  • Behoben: Die Studio-Vorschau scheiterte, wenn das Handshake-Secret nicht in der URL stand. Sie autorisiert jetzt über die Embed-Markierung, die der Server nach der Prüfung dieses Secrets ausstellt, sodass ein Reload oder eine clientseitige Navigation innerhalb des Frames weiter funktioniert und eine außerhalb von Studio geöffnete Storefront normal rendert statt No secret provided zu werfen. Die Markierung bleibt auf ein Projekt begrenzt. Der Projekt-Secret erreicht jetzt kein Client-JavaScript mehr und steht damit nicht länger im Hydration-Payload einer eingebetteten Seite.
  • Behoben: Die Analytics-Identitäts-Cookies überleben auf der geteilten Domain einer Hosting-Plattform. Eine von einem Host wie example.vercel.app oder example.pages.dev ausgelieferte Storefront begrenzte laioutr_vid und laioutr_sid auf das Plattform-Suffix selbst, was ein Browser als ungültige Cookie-Domain ablehnt — auf diesen Hosts existierte also keines der beiden Cookies und kein Besucher- oder Sitzungs-Token erreichte ein Ereignis. Die Cookie-Domain wird jetzt gegen den privaten Abschnitt der Public Suffix List aufgelöst, dieselbe Sicht, die ein Browser anwendet.
  • Behoben: Die konfigurierte Sortierung einer Query wird angewandt. Der auf der Query gespeicherte Wert fiel beim Bauen des Requests weg, sodass eine in Studio gesetzte Sortierung — oder eine als defaultSorting aus einem queryTemplateProvider zurückgegebene — den Query-Handler nie erreichte. Sie wird jetzt als sort der Query mitgeschickt; ein s-URL-Parameter hat weiterhin Vorrang, die konfigurierte Sortierung setzt also die Vorgabe und keine Festlegung. Links sind nicht betroffen.
  • Behoben: Der referrer eines Seiten-Ereignisses ist bei jedem Seitenaufruf eine absolute URL, nicht mehr nur beim ersten. Innerhalb einer SPA-Sitzung meldete er die vorige Route als nackten Pfad, was ein Ziel, das Referrer klassifiziert, als fehlerhaft liest statt als Besuch von derselben Seite.
  • Behoben: Eine Prop-Änderung im Studio aktualisiert die Vorschau wieder. Jede Änderung renderte die Seitenvariante neu, die PageRenderer beim Öffnen der Vorschau erfasst hatte, sodass sich ohne Reload nichts auf der Seite bewegte.
  • Ein Request-Span übernimmt einen eingehenden Trace-Kontext nur, wenn nichts weiter oben schon einen geöffnet hat. Wo eine HTTP-Instrumentierung bereits einen Server-Span begonnen hat — ein Node-Deployment, das an einen OTLP-Endpunkt liefert —, bleibt dieser Span der Elternknoten, statt den Request an den entfernten Aufrufer umzuhängen.
  • Das Start-Banner erscheint nicht mehr während nuxi prepare und nuxi typecheck, sondern nur noch, wenn die App tatsächlich bootet.

Hinweise für Entwickler (Breaking Changes)

  • @nuxtjs/robots wird nicht mehr installiert (0.38.2): robots.txt, der X-Robots-Tag-Header und der Route-Rule-Wert robots kommen jetzt aus der App @laioutr/app-essentials-seo — diese installieren, um sie zu behalten, und über deren App-Config statt über nuxt.options.robots konfigurieren. Ein Frontend mit weder dieser App noch einem eigenen @nuxtjs/robots-Install liefert keine /robots.txt aus (ein 404 sagt Crawlern, dass sie alles crawlen dürfen — genau das, was der bisherige Vorgabeinhalt aussagte), und jeder robots-Schlüssel in der nuxt.config oder in einer Route-Rule ist stillschweigend wirkungslos. robots-Meta-Tags auf Seitenebene sind nicht betroffen; sie kommen aus den SEO-Einstellungen der Seitenvariante.
  • Die spezialisierten Tracking-Composables sind entfernt (0.42.0): Statt const { trackAddToCart } = useProductInteraction(); trackAddToCart(payload) jetzt const { track } = useAnalytics(); track(AddToCart, payload).
  • ConsentAdapter-Vertrag (0.42.0): Die CMP-Apps zusammen mit @laioutr-core/frontend-core aktualisieren — ein gegen den alten Vertrag geschriebener Adapter installiert nicht mehr. registerAdapter plus activateAdapter werden ein synchrones setAdapter, das das Handle zum Entfernen des Adapters zurückgibt; deactivateAdapter entfällt. Wirft setup, warnt der Store und verwirft den Adapter. Storefront-seitig: consentStore.openConsentUi() und consentStore.openConsentUi('preferences') ersetzen showConsentOverlay() und renewConsent(). hasDecision() gibt boolean | undefined zurück — Code, der ein falsy-Ergebnis als Ablehnung gelesen hat, muss die beiden Fälle unterscheiden.
  • Einwilligung als Zwecke statt Cookie-Kategorien (0.42.0): ConsentManagementState und hasCategoryConsent sind entfallen. Aus { necessary, functional, statistics, marketing, unclassified } wird { necessary, functional, analytics, advertising, personalization }.
  • fillParams gibt undefined zurück, wenn ein Pflicht-Parameter keinen Wert hat, statt ihn mit einer Leere zu füllen (0.42.0). Die Leere fiel in den benachbarten Trenner zusammen — aus /:brand/p/:slug wurde /p/shoe, eine Adresse, die auflösbar aussah und es nicht war. Aufrufer müssen jetzt auf undefined prüfen. Endliche Mengen-Vorgaben (/:page(a|b)) und optionale Parameter (:lang?, :rest*) sind nicht betroffen; missingRequiredParams(path, params) benennt die fehlenden. Nachgelagert: Ein hreflang- oder Canonical-Link, dessen Parameter nicht gefüllt werden können, wird weggelassen statt abgeschnitten emittiert, und ein Sprachumschalter bietet die Startseite der Ziel-Domain statt eines defekten Pfads.
  • RenderI18nConfig.markets enthält nur noch verlinkbare Märkte (0.41.0). Die vollständige Liste inklusive Entwürfe liegt in RenderI18nConfig.allMarkets. Code, der einen Markt-Umschalter rendert, braucht keine Änderung und beachtet den Status automatisch; Code, der jeden konfigurierten Markt braucht (Routing, Host-Auflösung, Vorschau), muss auf allMarkets wechseln.
  • Lokale Dev-Hostnamen (0.41.0): Für Projekte auf *.app.laioutr.tech fällt dieses Suffix weg, statt in das Label gefaltet zu werden. Ein Markt auf example-shop.app.laioutr.tech ist jetzt unter example-shop.local.laioutr.tech erreichbar statt unter example-shop-app-laioutr-tech.local.laioutr.tech. Derselbe Host treibt Marktauflösung und Dev-Cookie-Domain, alle drei bleiben also im Gleichschritt — Lesezeichen und allowedHosts-Einträge, die die alte Form nannten, aktualisieren.
  • Ein nicht beanspruchter Pfad unter /api oder /.well-known antwortet mit einer kleinen HTML-404 (0.47.0) statt mit der vollständigen 404-Seite des Projekts samt Sektionen und Queries. Die Antwort trägt Cache-Control: public, s-maxage=3600, sodass ein CDN eine Wiederholungsanfrage ohne Funktionsaufruf beantwortet. Eine registrierte Server-Route und eine Datei in public/ gewinnen weiterhin, brauchen also keine Änderung. Ein /.well-known/…-Pfad, der von einer Nuxt-Page ausgeliefert wird, gewinnt nicht: nach public/ oder in eine Server-Route verschieben.
  • Tracing auf Vercel ist opt-in (0.46.0): LAIOUTR_OTEL_ENABLED=1 am Vercel-Projekt setzen, um es zu behalten.

Orchestr v0.38.2 – v0.45.0

Orchestr und Frontend Core werden im Gleichschritt veröffentlicht; diese Sektion führt nur, was ausschließlich im Orchestr-Changelog steht. Wie bei Frontend Core wurde v0.38.2 am 31. Juli veröffentlicht und erst im August dokumentiert.

Highlights

  • Nebenläufige Auflösung: Die Queries einer Anfrage, die Links einer Query und die Component-Resolver eines Entity-Typs lösen jetzt nebenläufig auf statt hintereinander. Eine Seite wartet auf ihren längsten Strang statt auf die Summe ihrer Arbeit. Der Server-Render einer Storefront-Startseite fiel von rund 724 ms auf rund 322 ms; die sechs Component-Resolver einer kalten Produktseite waren nach 378 ms fertig — hintereinander ausgeführt summieren sie sich auf 937 ms. Auf oberster Ebene laufen bis zu sechs Queries gleichzeitig. Chunks verschachteln sich dadurch: Ein Client, der sie nach path einordnet und Entity-Chunks wie ohnehin nötig nach ID mischt, ist nicht betroffen; einer, der sich darauf verlässt, dass Chunks nach Query gruppiert und in Request-Reihenfolge ankommen, ist es.
  • Ein fehlschlagender Link verwirft seine Query nicht mehr: Ein werfender Link-Handler ließ die ganze Query in einen Fehler-Chunk kollabieren und nahm dabei das Query-Ergebnis und jeden Entity-Chunk mit, den seine Geschwister-Links schon gestreamt hatten. Er meldet den Fehler jetzt an seinem eigenen Pfad, [queryId, linkToken], und der Rest der Query läuft durch. Scheitern mehrere Teile einer Query, meldet jeder seinen eigenen Fehler statt nur der erste.
  • Deklarativer Cache: Ein Query- oder Link-Handler schaltet seinen Cache mit einer TTL und einer Strategie ein, und mit nichts weiter — cache: { ttl: '1 day', strategy: 'ttl' }. Orchestr baut den ganzen Schlüssel: Token, Umgebung, angefragter Ausschnitt, Sortierung, Filter und Token-Eingabe. Gecacht wird standardmäßig der erste, ungefilterte Ausschnitt einer Liste; das Ende einer Liste ist kalt und eine Filterkombination unbegrenzt, beides wird deshalb aktiv hinzugenommen (pages: 'all', filters: ['filter.v.availability']) statt abgewählt. shouldBypassCache verweigert einen Request, den die deklarativen Optionen zulassen würden; es läuft danach und kann sie nur einengen.
  • validate entscheidet aus dem Ergebnis: Eine Query- oder Link-Cache-Konfiguration nimmt validate an, das mit dem Handler-Ergebnis aufgerufen wird, bevor es geschrieben wird. Gibt es false zurück, wird das Ergebnis ausgeliefert, aber nicht gespeichert. buildCacheKey entscheidet Cachebarkeit bereits aus dem Request, bevor der Handler läuft; das hier entscheidet sie aus dem Ausgang — ein degradierter Rückfall, der nicht für den Rest seiner TTL ausgeliefert werden darf, oder ein Ergebnis, dessen Erzeugung nichts kostete. Es steuert nur den Schreibvorgang: Ein bereits im Cache liegender Eintrag liefert weiter aus, bis seine TTL abläuft.
  • cacheKeys für handgebaute Schlüssel: useUserlandCache gibt einen nackten Storage ohne jedes Prefixing zurück. Ein dort gebauter Schlüssel trägt die Umgebung selbst mit, oder er liefert die Daten einer Storefront an eine andere, und er maskiert eine ID mit / oder :, oder unstorage schreibt sie in einen anderen Schlüssel um. cacheKeys.forClientEnv(clientEnv) und cacheKeys.escape(id) schließen das. cacheKeys.forEntityIds macht aus einer Menge von Entity-IDs ein kurzes Segment fester Länge, sodass ein handgebauter Schlüssel nicht mit der Seitengröße wächst — ein Handler, der IDs stattdessen verkettete, produzierte für 48 Produkte über zweitausend Zeichen, jenseits der 255 Bytes, die ein Dateisystem für ein Pfadsegment erlaubt, und hinein in das Request-Größenlimit eines gehosteten Redis. Query- und Link-Handler brauchen das nicht: Orchestr schlüsselt die Quell-IDs eines Links selbst.
  • Cache-Schlüssel sind rund 45 % kürzer: Gemessen an zwei Live-Storefronts fiel ein Component-Schlüssel auf einer von 144 auf 84 Bytes und auf der anderen von 121 auf 66, ein Produkt-Varianten-Link-Schlüssel von 136 auf 82, ein Page-Index-Schlüssel um 17 %. Der Schlüssel machte ein Drittel der Speicherkosten eines Component-Eintrags aus, und jeder Schlüssel wird bei jedem gebündelten Lesen erneut geschickt — die Ersparnis landet also auf der gespeicherten Größe und auf dem Request-Payload gleichermaßen. Kürzer wurden drei Dinge: der Namensraum, die Maskierung (zwei Bytes für die vier Zeichen, die unstorage umschreiben würde, statt drei Bytes für jedes Zeichen, das encodeURIComponent erkennt — eine Shopify-GID zahlte 15 Bytes für 5 Zeichen) und das Umgebungssegment, in dem Sprache, Währung, Markt und Vorschaustufe zu einem 8-Zeichen-Digest werden. Entity-Typ, ID, Component und Seitentyp bleiben lesbar.
  • Cache-Report auf Anfrage-Ebene: Eine Execution Summary trägt neben ihren Query-Zusammenfassungen einen Cache-Report für die Anfrage. Er beantwortet, was die Query-Zusammenfassungen nicht konnten — ob ein Component-Lesen als ein gebündelter Aufruf oder als viele einzelne ankommt, wie die Trefferquote eines bestimmten Components aussieht, und ob ein Hintergrund-Schreibvorgang verloren ging, als das Isolate einfror. Die Zähler werden nur für eine Anfrage erhoben, die options.dev.enableSummary gesetzt hat; jede andere zahlt nichts, die Payload-Messung eingeschlossen.
  • Passthrough ist auf den schreibenden Handler begrenzt: Jeder Handler einer Query teilte zuvor einen passthrough-Speicher. Ein Handler liest jetzt jedes Token, das seine Aufrufer gesetzt haben, und schreibt dorthin, wo nur die Handler unter ihm lesen können. Ein Link-Handler kann seinen eigenen Component-Resolvern also Daten übergeben, ohne dass sie den Rest der Query erreichen. Im Gegenzug sehen zwei Links, die dasselbe Token setzen, den Wert des jeweils anderen nicht mehr.

Fehlerbehebungen & Verbesserungen

  • Behoben: Ein TypeError: Cannot read properties of undefined (reading 'length') ließ jede Query scheitern, deren Link-Handler mit entity, entities oder einer einzelnen targetId antwortet. Die in 0.44.0 ergänzte Telemetrie zählte die Ziele eines Links über targetIds auf der Rohantwort des Handlers, was nur die targetIds-Form trägt. Die scheiternde Query gab keine Daten zurück und meldete einen Fehler-Chunk, sodass eine darauf gebaute Seite ohne ihre Produkte renderte.
  • Behoben: Query-, Link- und Entity-Component-Caches schlüsseln jetzt auf den Markt, Query- und Link-Caches zusätzlich auf das Paginierungs-Limit. Zwei Märkte, die Sprache und Währung teilen, lesen sich ihre gecachten Ergebnisse nicht mehr gegenseitig, und eine Anfrage nach 24 Elementen bekommt nicht mehr den Ausschnitt, den eine andere Seitengröße gecacht hat.
  • Behoben: Der angefragte Offset fehlte in jedem Query- und Link-Schlüssel, sodass ?offset=5&limit=24 das offset=0-Ergebnis ausgeliefert bekam. Und das getKeySuffix eines Component-Resolvers ersetzte den Umgebungs-Digest statt ihn zu erweitern, was zwei Märkte auf einem Eintrag kollidieren lassen konnte.
  • Behoben: Ein gestreamtes Query-Ergebnis wird gerendert, sobald es sich gesetzt hat, statt einmal pro Antwort-Chunk. Auf einer serverseitig gerenderten Seite renderte die erste clientseitige Navigation, die eine Query ausführte, bei jedem Chunk neu — einschließlich des Fensters, in dem die Entity-IDs eines Links angekommen sind, die Entitäten selbst aber noch keine Components melden. Sektionen, die diese Components lesen, renderten gegen diesen halb geladenen Zustand und warfen, was sich als sporadisch las, weil ein Retry meist nach dem Ende der Antwort landete.
  • Behoben: Ein Projekt, das seinen Cache auf ein echtes Backend richtet, behält es in der Entwicklung. Orchestr mountete bei jedem Dev-Boot ein In-Memory-LRU über seinen eigenen Cache-Namensraum, und dieser Mount ist spezifischer als der cache-Mount, den ein Projekt konfiguriert — er gewann also. Dev lief damit nie den Pfad, den Produktion läuft: keine Round-Trips, keine Serialisierungsgrenzen, und ein konfiguriertes Redis, das nichts empfing. Das LRU mountet weiterhin, wenn kein Backend konfiguriert ist.
  • Behoben: Ein pageIndex.locate-Lookup liefert die Seiten-Metadaten einer Sprache nicht mehr an eine andere. Das gecachte Ergebnis trägt in der Sprache aufgelöste meta, der Eintrag war aber allein auf den Markt geschlüsselt. Zwei Sprachen eines Markts kollidieren, sobald sie einen Route-Parameter teilen — was auf jedes Produkt zutrifft, dessen Slug eine SKU, ein Markenname oder eine unübersetzte Modellnummer ist. Der Schlüssel deckt jetzt die Sprache mit ab, und Vorschau- und veröffentlichte Ergebnisse teilen ebenfalls keinen Eintrag mehr.
  • Behoben: Cache-Schreibvorgänge überleben auf Vercel. Orchestr schreibt nach dem Antworten über event.waitUntil in den Cache, und auf Vercels Node-Runtime hielt nichts die Funktion am Leben, um sie zu beenden — Nitros Vercel-Preset setzt das event.context.waitUntil nie, nach dem Nitro selbst sucht, sodass der Schreibvorgang als freischwebendes Promise mit der Invocation starb. Gegen eine deployte Sonde auf einer kalten Instanz gemessen ging der verzögerte Schreibvorgang in vier von vier Versuchen verloren, der unmittelbare in zwei davon. Ein Nitro-Plugin liefert diesen Hook jetzt aus Vercels eigenem Request-Kontext, es braucht keine zusätzliche Abhängigkeit, tut abseits von Vercel nichts und tritt hinter jede Plattform zurück, die selbst einen bereitstellt.
  • Behoben: Devtools-Traces und Execution Summaries beschreiben, was tatsächlich passiert ist. Eine abgeschlossene Query meldete unknown für ihre ID und ihr Token, die fehlenden Entity-IDs eines Component-Resolvers fielen weg, und nebenläufig gestartete Spans erschienen als Kette — unter Promise.all laufende Initwares wurden ineinander verschachtelt dargestellt statt nebeneinander.
  • Behoben (0.38.2): listPagesFrom meldet seinen endCursor an jeder Abbruchposition, nicht nur an der take-Grenze. Ein Konsument, der früh aufhörte — etwa an einem Wall-Clock-Budget —, las endCursor als undefined und konnte das nicht von einer erschöpften Aufzählung unterscheiden, sodass ein Teil-Durchlauf als vollständig verbucht wurde. Zwei Felder machen den Ausgang eines Durchgangs eindeutig: exhausted ist das Abbruchsignal einer Akkumulationsschleife, progressed meldet, ob der Durchgang dauerhaft vorangekommen ist. Eine Schleife muss sowohl bei !progressed als auch bei exhausted stoppen, sonst wiederholt sich ein Durchgang, der nichts nimmt, endlos.

Hinweise für Entwickler (Breaking Changes)

  • Die Storage-Namensräume sind umgezogen: Ein Projekt, das einen Treiber unter cache mountet, deckt beide ab und braucht keine Änderung. Ein Projekt, das unter dem vollen Pfad mountet, schon — aus 'cache:orchestr:internal' wird 'cache:orch:i'. Ein auf einem alten Pfad belassener Treiber empfängt nichts, und der Cache fällt auf zurück, was cache bedient — im schlechtesten Fall ein Speichertreiber pro Isolate, der keinen Fehler meldet und zwischen Anfragen nichts hält.
  • buildCacheKey ist jetzt optional und liefert ein abschließendes Segment statt des ganzen Schlüssels. Ein Handler, der es behält, funktioniert weiter, und ein null-Rückgabewert verweigert den Cache nach wie vor.
  • Jeder Cache-Schlüssel ändert seine Form, in 0.43.1 wie in 0.45.0. Bestehende Einträge werden nie wieder gelesen und verfallen unter ihrer eigenen TTL — nach dem Deployment ist mit einem einmaligen Cold-Cache-Fenster zu rechnen; von Hand geleert werden muss nichts.
  • ExecutionSummary ist jetzt eine Union: vor dem Lesen der Query-Felder auf type einengen (if (summary.type === 'query') summary.linkSummaries;).
  • Chunk-Reihenfolge: Chunks verschiedener Queries verschachteln sich jetzt. Ein Client, der Chunks nach path liest, ist nicht betroffen; einer, der auf Gruppierung nach Query in Request-Reihenfolge baut, ist es.
  • Geteiltes passthrough entfällt: Ein Handler, der das Token eines Geschwister-Links gelesen hat, bekommt jetzt seinen eigenen Vorgabewert.

Laioutr UI v2.8.6 – v2.22.0

Highlights

  • Commerce-Ereignisse ohne Konfiguration: Produktlisten, Produktseiten, Warenkorb und Suche melden jetzt Commerce-Ereignisse. Die Instrumentierung ist immer an und hat keine Studio-Oberfläche. Produkt-Slider und das Produkt-Grid melden view_item_list, sobald sie auf dem Bildschirm sind — nicht bloß, wenn sie gerendert wurden. Ein Tile meldet select_item bei einer Navigation und add_to_cart, nachdem die Mutation aufgelöst ist. Eine Produktseite meldet view_item beim Laden und erneut bei jedem Variantenwechsel. Der Warenkorb meldet view_cart beim Öffnen, begin_checkout an der Handlungsaufforderung und für jede Mengenänderung eine Entnahme oder eine Hinzunahme, bemessen an dem, was sich bewegt hat, statt an dem, was übrig bleibt. Der Header meldet eine abgeschickte search, eine Suchergebnisseite view_search_results. Rohe Entitäten gehen in track(), und die Projektor-Registry macht daraus die Wire-Formen — keine Aufrufstelle baut ein Payload von Hand.
  • MediaFeed: Ein Vollbild-Feed für kurzformatige Medien im TikTok/Reels-Muster — vertikales Scroll-Snapping, immer nur ein spielendes Element, eine Aktionsleiste, ein Stummschalter und ein „Mehr lesen"-Sheet. Nur das sichtbare Element ist interaktiv, die anderen sind inert, sodass Tabben nie ein Steuerelement außerhalb des Bildschirms erreicht, und jedes Element trägt data-state="active" | "inactive". Rendern lässt sich der Feed aus Props (items + v-model:open) oder von irgendwo auf der Seite über useMediaFeedStore() steuern. Weil Browser Autoplay nur stumm erlauben, startet der Feed still und die obere Leiste trägt einen Weg zurück zum Ton. Mit urlParam wird das sichtbare Element mit der URL synchronisiert (?reel=<id>), sodass es sich teilen lässt und der Zurück-Button den Feed schließt statt die Seite zu verlassen — der Parameter trägt die Element-ID, nicht deren Position, ein Link übersteht also eine Umsortierung. Die zusammengesetzten Teile sind für eigene Kompositionen exportiert.
  • Neu im UI Kit an der Seite von MediaFeed: useOverlayHistory synchronisiert den Offen-Zustand und das aktuelle Element eines beliebigen Overlays mit einem Query-Parameter — Öffnen legt einen History-Eintrag an, Elementwechsel ersetzen ihn, und Zurück schließt das Overlay statt wegzunavigieren. AnimatedIcon spiegelt einen booleschen Aktivzustand, mit einer Überblendung beim Übergang und einer nach oben wegfliegenden Burst-Animation, in Vielfachen der Icon-Größe bemessen und über --animated-icon-active-color einfärbbar. AnimatedButton ist Button mit einem dauerhaften Pulsieren, das bei Hover, unter prefers-reduced-motion und über paused anhält. $count(1234) formatiert Zahlen kompakt für die aktuelle Sprache und beachtet eigene numberFormats; die Vorgabe-Abkürzung ist die von CLDR und unterscheidet sich je Sprache — Englisch kürzt ab tausend (1.2K), Deutsch erst ab einer Million. Badge bekommt eine glass-black-Variante für Badges über beliebigen Medien, und Media sowie MediaVideo bekommen v-model:currentTime als Geschwister von v-model:paused: Zuweisen springt, und das Element meldet seinen eigenen Fortschritt zurück.
  • Vier neue Locale-Bundles: Französisch, Italienisch, Spanisch und Polnisch — Storefronts in diesen Sprachen rendern ihre UI-Chrome nicht länger auf Englisch. ui-kit lieferte nur de, en und nl, und jede andere Sprache fiel auf en durch: 257 Meldungen über 33 Komponenten, darunter das Suchfeld, der Warenkorb, „Add to cart", „Sold Out", die Filter-Schublade und die Header-Beschriftungen für Konto, Wunschliste und Warenkorb. Der Seiteninhalt war übersetzt, die Chrome darum herum nicht. Die neuen Bundles decken alle 257 Schlüssel ab, Schlüssel für Schlüssel gegen en.ts verifiziert und mit jedem {placeholder}-Token erhalten. Zwei bestehende Lücken sind gleich mitgeschlossen: de fehlten sliderNavigation.play / .pause, nl der ganze mediaFeed-Block. Für bestehende Projekte ist das keine Verhaltensänderung — de, en und nl lösen genau wie vorher auf. Zuvor kam bereits (2.11.0) das niederländische Bundle für das UI Kit hinzu.
  • Das Produkt-Tile pinnt eine Variante nur dann in seinen Link, wenn der Connector sie autorisiert hat: Ein Tile in einer Liste, einem Grid oder einem Slider erzeugte für jedes Produkt /<slug>?variant=<variant-id>. Der Wert kam aus defaultVariant, den die meisten Connectoren aus dem Lagerbestand ableiten — dasselbe Produkt änderte also seine URL, sobald eine seiner Größen ausverkauft war. Eine abgeleitete Vorgabe bleibt jetzt aus dem Link, und jede Listen- und Slider-URL dieser Produkte ändert sich. Eine autorisierte Vorgabe ist die Wahl eines Händlers und wandert nicht mit dem Bestand, der Link trägt sie also weiter. Ein ausdrückliches ?variant= in einem geteilten Link, einer Anzeigen-Landing-URL oder einem Wunschlisten-Eintrag bleibt unangetastet. Passend dazu öffnet eine ohne ?variant= aufgerufene Produktseite jetzt die Vorgabe-Variante des Produkts statt dessen erster, und ein Farb-Swatch wird nur für eine autorisierte Vorgabe-Variante als ausgewählt markiert. ProductDefaultVariant.origin trägt die Unterscheidung.
  • Produkt-Tiles zeigen die Farb-Swatches ihres Produkts: Die Swatch-Reihe existierte, wurde aber nie gefüllt und war damit immer leer. Tiles lesen die Options-Achsen des Produkts jetzt direkt und laden für eine Liste nicht mehr jede Variante, was die Datenmenge eines Produkt-Grids im Browser reduziert. Ein ausverkauftes Produkt deaktiviert seinen In-den-Warenkorb-Button weiterhin; dieser Zustand kommt jetzt aus der Vorgabe-Variante des Produkts statt aus einer geladenen Varianten-Entität. ProductTileBasic.sizeVariants trägt einen Eintrag pro Wert auf der Größen-Achse des Produkts, jeder adressiert die Variante, die der Connector für diese Größe benennt, und ist deaktiviert, wenn keine kaufbare Variante sie trägt.
  • Web-Fonts sind konfigurierbar: fonts am Modul @laioutr-app/ui erreicht die gesamte @nuxt/fonts-Konfiguration — families, defaults, providers und preload. Ein gesetzter Wert ersetzt die Vorgabe, Arrays eingeschlossen; false überspringt das Modul, für eine Storefront mit eigenen @font-face-Regeln. Fonts werden nur noch als woff2 ausgeliefert: Ein Provider beantwortet eine Anfrage pro User-Agent und gibt jede Format-Stufe zurück; die alten woff-Faces tragen keine unicode-range, passen also auf jedes Zeichen, und stehen zuletzt — was einen modernen Browser dazu bringt, sie anstelle der woff2-Dateien zu laden. Ergänzend greift fonts.defaults.subsets jetzt überhaupt: Zuvor erreichte jedes Subset, das ein Provider zurückgab, das Stylesheet, und ein unbenutztes lud, sobald ein einziges Zeichen es beanspruchte — auf einer Storefront 36,7 KB Glyphen, die nie rendern, auf jeder Seite.
  • Slider und Videos hydrieren und laden spät: CommonSwiper hydriert, sobald er in die Nähe des Viewports kommt, statt im ersten Hydration-Durchgang — Karussells weit unten auf einer Seite initialisieren Swiper beim Laden also nicht mehr. Das servergerenderte Markup ist unverändert: Ein Slider sieht vor seiner Hydration genauso aus, er antwortet nur noch nicht auf Pfeile, Bullets oder Autoplay. CommonSwiperEager importieren, um das vorige Verhalten zu bekommen. Über Media gerenderte Videos warten ebenfalls, bis sie in Viewport-Nähe sind, bevor sie laden; Videos unter einem MediaAboveTheFold-Provider sind ausgenommen, andernorts überschreibt videoPreload es pro Video. Und ein Bild über dem Falz emittiert seinen Preload-Link jetzt mit fetchpriority="high" — das <img> trug den Hinweis schon, aber der Preload-Link startet den Abruf und trug keinen, sodass der Browser das LCP-Bild mit Standardpriorität holte.
  • Banner: Höhe, Hintergrundfarbe und zweidimensionale Ausrichtung: Alle drei CTA-Banner-Sektionen — Banner Basic, Banner Integrated und Banner Showcase — bekommen eine Sizing-Steuerung (feste Höhe, responsive Höhe oder Seitenverhältnis), um dem Banner eine bestimmte Höhe zu geben. Die Content-Ausrichtung von Banner Basic wird zweidimensional (vertikal plus horizontal, etwa bottom-left), und Banner Integrated bekommt eine Vertikal-Ausrichtung, sodass Inhalt an jeder Kante sitzen kann — auch unten. Die Vertikal-Ausrichtung greift erst, wenn der Banner eine bestimmte Höhe hat. Dazu bekommen alle drei, in ihrer Sektions- wie in ihrer Block-Variante, eine Hintergrundfarbe für das innere Banner-Tile, unabhängig vom äußeren Backdrop der Sektion; Text- und Icon-Kontrast passen sich der gewählten Farbe automatisch an. Bestehende Sektionen rendern unverändert.
  • Container bekommt eine Tablet-Stufe: columnsTablet und gapTablet fügen eine Stufe zwischen Mobil und Desktop ein. Spaltenzahl und Gap lösen jetzt in drei Stufen auf — mobil (unter 600px), Tablet (600–1279px), Desktop (ab 1280px) —, ein Breakpoint-Schema, das mit dem Sizer geteilt wird. Beide fallen ungesetzt auf ihren Mobil-Wert zurück. Die Container-Sektion bekommt dafür die Steuerungen Tablet columns und Gap (Tablet).
  • Ganzflächig klickbare Banner und Hero-Slides: HeroSlide, BannerBasic, BannerIntegrated und BannerShowcase nehmen ein href an, das die ganze Komponente zu einem Link macht, gerendert als transparente ganzflächige Überlagerung. Es aktiviert sich nur, wenn kein CTA-Button vorhanden ist, sodass eine Komponente entweder button-getrieben oder ganzflächig klickbar ist — nie beides, was interaktive Elemente in einem Anker verschachteln würde. Die zugehörigen Blöcke und, ab 2.14.0, auch SectionBannerBasic bekommen dafür ein Link-Feld.
  • Neue Blöcke und Steuerungen für Studio: BlockBasicTable bringt die BasicTable-Molekül als Studio-Block, mit einem autorisierten Array aus Label/Wert-Paaren und der outlined/plain-Variante als Design-Stil; es ist eigenständig und wird damit in jeder Sektion angeboten. BlockProductSlider macht den Produkt-Slider als eigenständigen Block verfügbar, sodass er auch in einen Container-Slot passt — Konfiguration, Datenbindung und Rendering sind mit SectionProductSlider identisch. ProductDetailButtonGroup und BlockProductDetailButtonGroup stapeln vollbreite Handlungsaufforderungen auf der Produktdetailseite. Die Blöcke Mega Menu, Mobile Menu (Shop) und Side-by-Side Menu bekommen eine Start Level-Steuerung, die den geholten Kategoriebaum um die konfigurierte Zahl an Ebenen absteigt — für das Überspringen eines synthetischen Upstream-Wurzelknotens, ohne Query oder Connector zu ändern. BlockPagination versteckt sich, wenn die Ergebnismenge auf eine Seite passt, mit einem showOnSinglePage-Schalter, um es zu behalten. Und BlockFooterMenu-Einträge bekommen eine Liste Visible in markets: leer heißt überall sichtbar, was jedes vorher autorisierte Menü tut. Die beiden Teile dahinter sind für andere Komponenten exportiert — visibleMarketsField für das Schema und inMarket() für den Render.
  • Captions bekommen eine Text-Size-Einstellung, so verdrahtet wie Heading und Subline schon. CaptionFlag hatte seine Größe fest verdrahtet; sie ist jetzt optional, der aus der Variante abgeleitete Wert bleibt der Rückfall. Die Optionsliste ist bewusst eine Teilmenge der geteilten Text-Größen-Skala: Die Caption-Utilities kommen aus den --caption-*-Tokens, die bei xl enden, 2xl und größer werden deshalb nicht angeboten statt angeboten und stillschweigend wirkungslos zu sein. Die Einstellung landet auf dem geteilten captionVariant-Style-Dekorator, jede Sektion und jeder Block, der ihn schon nutzt, bekommt die Steuerung also mit.
  • MediaVideo meldet Meilensteine: Ein milestone-Ereignis feuert, wenn die Wiedergabe jedes Viertel des Videos überschreitet, und trägt { milestone, currentTime, duration }, wobei milestone der erreichte Anteil ist — 0.25, 0.5, 0.75 oder 1, je einmal pro montiertem Player. useMediaMilestones(playback, onMilestone, config?) ist der kopflose Detektor dahinter, für dasselbe Verhalten an einem nicht-nativen Player; er nimmt reaktive currentTime und duration, weiß nichts über Medienelemente oder Analytics und akzeptiert { fractions } anstelle der Viertel.
  • SocialShare meldet die gewählte Plattform: Es emittiert share mit der Plattform, die ein Besucher gewählt hat, und BlockSocialShare meldet das als web/share-Ereignis. Das Payload trägt allein method — die Plattform-ID (facebook, x, linkedin, pinterest, email). Der Block bindet an keine Entität, geteilt wird also die Seite, und der Umgebungs-Seitenkontext legt deren URL, Pfad und Typ ohnehin auf jedes Ereignis. Jeder Button ist ein ausgehender Link auf den Dialog der Plattform, das Ereignis markiert also den Klick und keinen abgeschlossenen Teilen-Vorgang.
  • Login und Konto: Wer sich in den Produktbewertungen anmelden will, wird jetzt zu seinem Konto geschickt. Die Login-Aufforderung löst über die Aktion ecommerce/auth/login-oauth auf und funktioniert damit mit jedem Connector, der sie beantwortet — einer OAuth-Autorisierungs-URL, wo Konten gehostet sind, oder einem Link auf die Konto-Seite, wo die Storefront ihre eigene rendert. Der Konto-Button und die Login-Aufforderung des Bewertungsbereichs leiten statt eines Popups auf den Kunden-Login um und tragen den aktuellen Pfad mit, sodass der Kunde dorthin zurückkommt. Dafür braucht es einen Connector, der die returnTo-Eingabe von AuthLoginOauthAction liest; gegen einen, der sie ignoriert, funktioniert der Login trotzdem. Das Login-Popup scheitert außerdem in Safari und Firefox nicht länger stillschweigend: Es wird geöffnet, während der Klick noch eine vertrauenswürdige Geste ist, und fällt auf eine ganzseitige Umleitung zurück, wo ein Popup ohnehin blockiert wird. Und der Warenkorb eines angemeldeten Kunden trägt jetzt seine Identität, sodass der Checkout authentifiziert öffnet und die entstehende Bestellung an sein Konto hängt.
  • Ein Link mit mehreren Optionswerten öffnet die richtige Variante: Eine Produktdetail-URL, die mehrere Optionswerte benennt, öffnet die Variante, die alle trägt. Zuvor genügte jeder einzelne passende Wert, sodass ?variant=Rot&variant=XL ein rotes Produkt in der falschen Größe öffnen konnte. Eine URL, die einen Wert benennt, öffnet weiterhin die erste Variante, die ihn trägt.
  • Child-Categories-Link und Kategorie-Karten aus einer Datenquelle (2.8.6): Neu ist das ChildCategoriesLink-Token (ecommerce/category/child-categories) in canonical-types. Der Shopify-Connector implementiert es, indem er das Navigationsmenü liest (neue Option categoryMenuHandle, Vorgabe main-menu), den Knoten der Quell-Collection findet und dessen Collection-Kinder als eingebettete Category-Entitäten zurückgibt, lokalisiert über @inContext. SectionCategoryCardSlider bekommt dazu eine optionale Kategorie-Query, deren Entitäten als Karten rendern; manuell gesetzte Slot-Karten haben weiterhin Vorrang.
  • MenuSideBySide bekommt einen below-separator-Slot auf der Wurzelebene. Der Bereich unter dem Trenner des Wurzel-Drawers renderte MobileMenuListItems aus dem rootMenuItems-Prop und nichts weiter, sodass eine Storefront, deren Schnellzugriffe gestaltete Buttons statt Listenzeilen sind, dort nichts unterbringen konnte. Der Slot wird auf jeder Ebene zwischen Konsument und Ziel weitergereicht, jeweils nur, wenn ein Konsument ihn tatsächlich befüllt.
  • ProductListingGrid nimmt einen productTile-Scoped-Slot an, gebunden als { product }, passend zu dem, den ProductSlider schon anbietet; sein Rückfall ist das vorher gerenderte Tile. CartSummaryBox emittiert checkout, wenn seine Handlungsaufforderung geklickt wird, und CartSheet reicht es weiter — der Button behält sein href, die Navigation ist also unverändert.
  • CartSheet kann gesagt bekommen, wie viele Artikel seine Header-Pille melden soll, über das neue optionale cartContent.itemCount. Die Pille war fest auf die Zahl der Zeilen verdrahtet, was nur bei Menge 1 pro Zeile der Artikelzahl entspricht — eine Storefront, deren Header-Warenkorb-Badge Einheiten zählt, zeigte für denselben Warenkorb also zwei verschiedene Zahlen: Zwei Produkte mit Menge 3 lasen im Header 6 und in der Schublade 2. Ohne den Wert bleibt der Rückfall auf die Zeilenzahl.

Fehlerbehebungen & Verbesserungen

  • Behoben: QuantityPicker entfernte einen Artikel versehentlich. Es emittierte delete, wenn das Mengenfeld am Minimum den Fokus verlor oder der Minus-Button dorthin heruntersprang — ein Klick in das Mengenfeld einer Warenkorbzeile mit Menge 1 entfernte die Zeile also. delete feuert jetzt nur noch für den Löschen-Button am Minimum oder für einen darunter eingegebenen Wert.
  • Behoben: Die Produkt-Tiles in einem Produkt-Grid zeigen ihren In-den-Warenkorb-Button, Marke, Beschreibung, Flags und Bewertung wieder. ConnectedProductTileBasic deklarierte jeden Schalter als optionales Boolean ohne Vorgabe, sodass Vue einen ungesetzten auf false warf und die Komponente dieses false an ProductTileBasic weiterreichte — womit es dessen eigenes default: true schlug. BlockProductsListing übergibt keinen dieser Schalter, jedes Tile in einem Grid renderte also mit allen fünf ausgeschaltet.
  • Behoben: Ein Preis in einem Commerce-Ereignis wird für die Variante gebildet, die das Ereignis benennt, nicht für das Produkt, zu dem sie gehört. Der eigene Preis eines Produkts ist ein „ab"-Preis über seine Varianten, und die Projektion meldete schon SKU und Namen der aktiven Variante — beides mit dem Produktpreis zu paaren bepreiste also eine Variante als eine andere und verstand bei breiter Variantenspanne den Betrag um eine Größenordnung. projectAnalyticsProduct liest jetzt die Preise der aktiven Variante und fällt nur dort auf die des Produkts zurück, wo die Variante keine hat. Ein Variantenpreis trägt nie den Streichpreis des Produkts, weil das einen Rabatt erfinden würde. Der add_to_cart-Wert eines Tiles wird aus der tatsächlich hinzugefügten Variante berechnet.
  • Behoben: Produkt-Tiles rendern weiter, wenn einer Variante die Components fehlen, die sie lesen. Ein Query-Ergebnis kann legitim eine Produktvariante tragen, deren Components nicht aufgelöst wurden; der geteilte Produkt-Tile-Mapper las options.selected und availability.status von jeder Variante, sodass eine einzige solche Variante das ganze Produkt-Grid, den Produkt-Slider oder den Produkt-Slider – Showcase hinter dessen Error Boundary nahm. Solche Varianten werden jetzt übersprungen, und das Tile rendert mit einer kurzzeitig kürzeren Größenliste.
  • Behoben: Der In-den-Warenkorb-Button auf Tiles in der Sektion Produkt-Slider – Showcase tat beim Klicken nichts — kein Spinner, kein Toast, keine Warenkorb-Anfrage —, während derselbe Button im einfachen Produkt-Slider funktionierte. Beide verhalten sich jetzt gleich: Ladezustand pro Tile, Erfolgs- und Fehler-Toasts, aktualisierte Warenkorb-Zahl. ProductSliderShowcase bekommt außerdem einen optionalen productTile-Slot.
  • Behoben: CategoryCardSlider und CategoryCardGrid rendern ihr nodes-Prop und ihren Default-Slot zusammen, nodes zuerst. Zuvor ersetzte der Default-Slot nodes vollständig, und weil die Kategorie-Karten-Sektionen immer einen übergeben, renderte nodes durch sie hindurch überhaupt nie — ein an eine Kategorie-Datenquelle gebundener Kategorie-Karten-Slider zeigte also einen leeren Slider.
  • Behoben: Das Filter-Off-Canvas-Akkordeon öffnete das falsche Panel. FilterOffCanvasAccordionItem nutzte activeValue (den Anzeigetext) als reka-AccordionItem-Identität, sodass jeder nicht ausgewählte Listen- und Bereichsfilter value="" teilte und ein Klick auf irgendeinen Trigger den ersten öffnete und die anderen überlappte. Das Item nimmt jetzt ein eigenes, eindeutiges value-Prop für die Identität.
  • Behoben: Header- und Schnellfilter-Reihe der Filterleiste richten sich auf großen Bildschirmen an den umgebenden Seiteninhalt aus. Ihr horizontales Padding war ein festes --spacing-ml, schmaler als das Padding des Seiten-Containers, sodass beide Reihen ab lg leicht innerhalb der Inhaltsspalte saßen. Sie lesen jetzt --container-padding.
  • Behoben: BlockMedias Sizing-Einstellung gilt nur noch für das Bild, nicht für Bild plus Beschreibung. Sizer umschloss den ganzen Block, sodass ein Seitenverhältnis von 16/9 das Verhältnis von Bild und Bildunterschrift war: Das Bild verlor genau die Höhe, die die Unterschrift nahm, und eine längere Unterschrift schrumpfte es weiter. MediaPreview nimmt jetzt ein optionales sizing und wendet es auf seine Medienbox an, mit der Beschreibung darunter und außerhalb der bemessenen Box.
  • Behoben: Ein Absturz auf einer Hero-Slide, die eine Caption zeigt. SectionHeroSlider rief resolveCaptionVariant ohne Import auf, und ui-app registriert kein Auto-Import-Verzeichnis, sodass das Bauen der Slide-Props einen ReferenceError warf. Nur eine Slide mit mindestens einer sichtbaren Caption erreichte den Aufruf, weshalb es latent blieb.
  • Behoben: BrandHero zeigt eine Hintergrundfarbe, wenn kein Hintergrundbild gesetzt ist. fallbackColor wurde die ganze Zeit an MediaStage übergeben, aber finalMedia fiel bei leerem background auf die dekorative Hero-SVG des Themes zurück — die Farbe war also hinter einem deckenden Bild gemalt. Eine ausdrückliche Farbe hat jetzt Vorrang; die Theme-Vorgabe gilt weiter, wenn keine Farbe gesetzt ist. Das Feld heißt in SectionBrandHero jetzt „Background Color". Außerdem wird BrandHeros description zu Rich Text: Das Feld war ein textarea, nahm also nur getippte Texte — und auf einer Markenseite ist sein ganzer Zweck, die Beschreibung der Marke aus dem Katalog zu tragen. Als richtext bietet Studio eine Datenquelle darauf an; das Prop weitet sich auf string | HtmlFragment, Aufrufer mit einem einfachen String funktionieren unverändert.
  • Behoben: Das gefüllte Herz-Icon (essentials/heart-filled) renderte bei size="s" und size="m" nicht.
  • Ergänzt: Der String review.logIn in allen sieben ausgelieferten Sprachen — der Button der Bewertungs-Login-Aufforderung war fest auf Englisch verdrahtet.
  • Der Produkt-Slider – Showcase setzt Show Product Flags jetzt standardmäßig auf an, passend zum normalen Produkt-Slider (nur neue Sektionen; bestehende Konfigurationen sind nicht betroffen). Und productTileMapper gibt ein sizeVariants-Array aus ({ value, label, disabled }, nach Varianten-ID geschlüsselt, disabled für ausverkaufte Größen); das Varianten-Limit der geteilten Query steigt dafür von 5 auf 30, sodass vollständige Größenläufe nicht länger abgeschnitten werden.

Hinweise für Entwickler (Breaking Changes)

  • AuthLoginOauthAction (ecommerce/auth/login-oauth) gibt keine nackte authorizationUrl mehr zurück (2.11.0), sondern eine diskriminierte Union: die OAuth-Autorisierungs-URL, wenn der Kunde keine Sitzung hat, und einen Link auf sein Konto, wenn er eine hat. Vor dem Lesen auf type einengen. Das Konto-Ziel ist ein Link statt einer URL-Zeichenkette, das Frontend löst es also auf — Shopify gibt einen externen Link auf seine gehostete Konto-Seite zurück, ein Connector, dessen Konto eine Storefront-Seite ist, einen pageType-Link aus demselben Vertrag.
  • Review meldet die Absicht statt sie selbst zu behandeln (2.17.0): Es emittiert submit mit den Formulardaten, wenn das Bewertungsformular abgeschickt wird, und login, wenn der Besucher die Login-Aufforderung bestätigt. Der primäre Button der Login-Aufforderung öffnet keinen eigenen Bestätigungsdialog mehr — er emittiert login, und der Konsument entscheidet, wohin der Besucher geht. Ein Review ohne login-Listener zeigt die Aufforderung, kann aber niemanden anmelden.
  • CartListItem markiert eine Zeile, deren Ware nicht gekauft werden kann (2.16.0): Es nimmt ein neues isSoldOut-Prop, rendert ein „Out of stock"-Badge und trägt data-unavailable an seiner Wurzel für das Styling; ConnectedCartSheet setzt es aus der Verfügbarkeits-Komponente des Warenkorb-Artikels. Eine eigene Locale muss den neuen Schlüssel cart.itemSoldOut liefern.
  • SectionCategoryCardSlider paddet sich nicht mehr selbst (2.14.0): Die Sektion überschrieb das geteilte paddingField mit default: 's', sodass jede Instanz mit einem vertikalen Sektions-Padding startete, das niemand angefordert hatte — und eine Sektion, die sich selbst paddet, kann nicht bündig an ihre Nachbarin gesetzt werden. Verhaltensänderung: Bestehende Instanzen, die das Padding-Feld nie angefasst haben, verlieren dieses Padding. Ausdrücklich auf S setzen, um den alten Look zu behalten. Sieben weitere Sektionen tragen dieselbe default: 's'-Überschreibung und bleiben hier bewusst unangetastet.
  • Der Desktop-Gap von Container schaltet bei 1280px statt bei 800px (2.10.0): Container mit einem ausdrücklichen Desktop-Gap und ohne Tablet-Gap nutzen zwischen 800 und 1279px jetzt den Mobil-Gap, wo sie vorher den Desktop-Gap nutzten — dieselbe Breakpoint-Vereinheitlichung, die der Sizer schon anwendet. Das Spaltenverhalten ist nicht betroffen.
  • BlockPagination versteckt sich auf einseitigen Ergebnismengen (2.13.0). Verhaltensänderung: Storefronts, die die Steuerung auf einseitigen Listen zeigten, tun das nicht mehr. showOnSinglePage in der Rules-Gruppe des Blocks einschalten, um das alte Rendering zu behalten — lohnend dort, wo die Höhe einer Liste sich nicht ändern soll, während Filter das Ergebnis einengen, weil ein erscheinendes und verschwindendes Steuerelement alles darunter verschiebt.
  • BrandHero mit Hintergrundfarbe und ohne Bild wechselt von der Theme-Grafik zu dieser Farbe (2.15.0). Das ist der gemeldete Fehler, aber die Änderung ist sichtbar — die Farbe leeren, um die Grafik zu behalten.
  • Jede Listen- und Slider-URL von Produkten mit abgeleiteter Vorgabe-Variante ändert sich (2.20.0), weil das ?variant= daraus verschwindet. Ausdrücklich geteilte Links bleiben unangetastet.

Cockpit (Studio) August 2026

Highlights

  • Studio hält den geöffneten Stand in der URL: Geöffnete Seite, Auswahl und Vorschau stehen in der URL — ein Reload oder ein geteilter Link kehrt genau dorthin zurück. Ergänzend werden auch der gewählte Markt und die gewählte Sprache in der URL gehalten, sodass ein Reload oder ein geteilter Link denselben Markt und dieselbe Sprache öffnet statt der Projekt-Vorgabe.
  • Märkte haben einen Status: Entwurf oder Aktiv, neue Märkte starten als Entwurf, und die Markt-Karten zeigen Entwurfs- und Standard-Badges. Ein Projekt kann seinen Standardmarkt aus seinen aktiven Märkten wählen, und der Selektor benennt den Rückfall, der ohne Wahl gilt.
  • Sortierung im Query-Editor: Eine Sortierung auf einer Query ändert die Vorgabe-Reihenfolge ihrer Ergebnisse.
  • Link-Felder aus einem Query-Ergebnis: Ein Link-Feld kann sein Ziel jetzt aus einem Query-Ergebnis nehmen, so wie Medienfelder das schon können.
  • Abandoned-Props-Panel in den Studio Devtools: Ein neues Panel meldet gespeicherte Props, deren Feld eine Sektion oder ein Block nicht mehr definiert oder zu deren Wert das Feld nicht mehr passt — und kann sie entfernen.
  • Redirects auf der Vercel-Edge: Ein Managed-Vercel-Deploy veröffentlicht seine Redirects auf der Vercel-Edge, die sie vor jeder Route im Deployment beantwortet — eine alte URL kostet damit keinen Funktionsaufruf mehr.
  • Vorschau im Desktop-Rahmen: Die Vorschau öffnet jetzt im Desktop-Rahmen (1920) statt sich auf die Breite des Editor-Fensters zu strecken.

Fehlerbehebungen & Verbesserungen

  • Behoben: Der Import einer Redirect-CSV mit mehr als 256 Zeilen funktioniert.
  • Behoben: Projekte mit mehr als 1000 Redirects deployen jeden Redirect, exportieren die vollständige Liste als CSV und importieren sie ohne Duplikate wieder ein.
  • Behoben: Das Bearbeiten von Limit, Label, URL-Alias und Eingaberegeln einer Query in Studio wird gespeichert.
  • Behoben: Studio meldet einen abgelehnten Projekt-Secret und benennt das Projekt, das der Host ausliefert — samt Link auf dieses Projekt.
  • Behoben: Das Leeren eines Zahlenfelds setzt es auf seine Vorgabe zurück, und ein auf 0 gesetztes Feld zeigt die 0.
  • Behoben: Die Billing-Einstellungen sind immer erreichbar, das Billing-Portal lässt sich also öffnen, bevor eine Zahlungsmethode hinterlegt ist.

Die vollständigen Changelogs mit allen technischen Details findest du unter docs.laioutr.io/getting-started/changelogs.

Mehr interessante Frontend Artikel

Praxiswissen für Frontend-Entwicklung, smarte Agenten und Headless

Shopify
Shopify ist eine Commerce-Plattform zum Verkaufen online und im stationären Handel.
Shopware
Shopware ist eine flexible E-Commerce-Plattform aus Europa für Produktkataloge und Omnichannel-Commerce.
Planned
Scayle
SCAYLE ist eine Commerce-Engine, mit der Marken und Händler ihr Geschäft skalieren.
Planned
Commerce Layer
Commerce Layer ist eine Headless-Commerce-Plattform, um Bestände und Kataloge online verfügbar zu machen.
Planned
Salesforce Commerce Cloud
Salesforce Commerce Cloud ist eine cloudbasierte Enterprise-Commerce-Plattform für Unternehmen jeder Größe.
Commercetools
Commercetools ist eine SaaS-basierte, headless E-Commerce-Plattform mit weltweitem Einsatz.
Sylius
Sylius ist ein entwicklerfreundliches E-Commerce-Framework für B2C- und B2B-Shopping-Erlebnisse.
OXID eShop
OXID eShop ist eine erweiterbare Commerce-Plattform für komplexe B2B- und B2C-Anforderungen.
Emporix
Emporix ist eine composable, API-first Commerce-Plattform für skalierbare B2B- und B2C-Szenarien.
Adobe Commerce
Adobe Commerce ist eine Enterprise-Commerce-Plattform für komplexe, globale B2C- und B2B-Szenarien.
Coming Soon
VTEX
Cloud-native, composable Commerce-Plattform für B2B und B2C im großen Maßstab.
Planned
Spryker
Composable Commerce-Plattform für anspruchsvolle B2B- und B2C-Geschäftsmodelle.
Planned
SAP Commerce Cloud
Enterprise-Commerce-Plattform für komplexe Kataloge, Preismodelle und Omnichannel-Journeys.
Planned
Websale
Stabiles, enterprise-taugliches Commerce-Backend für komplexe Handelsumgebungen.
Planned
Intershop
Enterprise-Commerce-Plattform für komplexe B2B- und B2C-Geschäftsmodelle.
Planned
Magento 2
Weit verbreitete, erweiterbare Commerce-Plattform für B2C- und B2B-Szenarien.
Planned
B2Bsellers
B2B-Suite für Shopware, die den Online-Shop zur professionellen B2B-Commerce-Plattform macht.
Planned
Saleor
Open-Source-, API-first-Commerce-Plattform auf GraphQL-Basis für Custom-Storefronts.
Planned
Prestashop
Open-Source-Commerce-Plattform für kleine und mittlere Händler in Europa und darüber hinaus.
Planned
Vendure
Vendure ist eine Headless-Commerce-Plattform für Unternehmen mit komplexen Anforderungen.
Planned
Patchworks
Patchworks ist eine Low-Code-iPaaS, die E-Commerce, ERP, WMS, 3PL und Marktplätze verbindet.
Planned
HCL Software
Enterprise-Suite für digitalen Commerce und Experience mit hoher Konfigurierbarkeit.
Book a demo mobile
Strategie-Gespräch

Bereit, Dein Frontend zur Steuerebene zu machen?

Zeig uns Deinen Stack, Deine Roadmap, Dein Replatforming-Szenario, wir zeigen Dir, wie Laioutr passt, was es kostet und wie schnell ihr live geht.

"Nach 30 Minuten wussten wir, dass Laioutr unser Replatforming machbar macht." - Daniel B., CEO, hygibox.de

SEO / GEO / AEO Ready
Performance & Core Web Vitals
WCAG 3.0 Ready
Tracking & Analytics
Brand Consistency