Sådan viser du fri fragt-bjælke og forslag i dit eget tema
Bjælken for fri fragt og forslag til varer, der giver fri fragt, er nu bygget ind i standardtemaerne. Bygger du dit eget tema, kan du vise det samme med én variabel, én metode, og en lille formular, og her er guiden trin for trin.
"Hvor meget mangler jeg for at få fri fragt?" Det spørgsmål stiller kunden sig selv i kurven, og svaret afgør tit, om der kommer én vare mere i kurven, eller om kunden betaler fragten. Butiksejere har bedt om en bjælke, der viser det. Nu er den bygget ind i standardtemaerne sammen med forslag til varer, der lukker hullet med ét klik.
Montana, MontanaB2B, Delaware, DelawareDK, Alaska2, og California har det bag to temaindstillinger. Bygger du kundetemaer, får dine temaer det ikke automatisk. Til gengæld er det meste arbejde gjort på serveren: Shoporama regner afstanden til fri fragt ud med de samme regler som kassen og vælger forslagene. Dit tema skal bare vise dem. Her er guiden, trin for trin.
Sådan bygger du det ind
1. Læs forløbet
Alt starter med variablen $free_shipping_progress. Den er sat på alle sider i webshoppen, men brug den kun i kurven og på leveringstrinnet, som aldrig caches. Andre sider kan ligge i den delte sidecache, fx i temaer bygget på Montana, og så ser alle besøgende den første besøgendes tal. Eksemplerne her er skrevet til Smarty 4. $webshop->getFreeShippingProgress() giver det samme som variablen, hvis du skal bruge det et sted, hvor variablen ikke er sat. Når der er noget at vise, er den et array med fem felter:
- limit: grænsen for fri fragt ("Fri fragt over") på leveringslandet eller den valgte fragtmetode, i landets valuta.
- price: det beløb, der måles mod grænsen, regnet som i kassen. Tilkøb, der er undtaget fra grænsen, tæller ikke med, en rabatkode med "Medregn ikke i fri fragt-grænsen" (fx et gavekort) trækkes ikke fra, og værdien af brugte point trækkes fra.
- remaining: hvor meget kunden mangler. 0, når fri fragt er nået.
- percent: et heltal fra 0 til 100 til bjælken.
- reached: sand, når kunden har fri fragt, også når det skyldes en rabatkode eller loyalitetsprogrammet.
Variablen er null, når der ikke er noget at vise: når kurven er tom eller kun har tilkøb, når der ingen grænse er på landet eller fragtmetoden, når fragtmetoden har "Aldrig gratis fragt", og når en vare i kurven har "Aldrig fri fragt". Start derfor komponenten med et null-check og teksten til kunden. Separatorerne og valutaen følger kundens land, så tallene også ser rigtige ud i en butik, der sælger i flere lande:
<{if $free_shipping_progress}>
<{$fs = $free_shipping_progress}>
<{$fs_decimal = $webshop->getDecimalSeparator()}>
<{$fs_thousand = $webshop->getThousandSeparator()}>
<{$fs_currency = $webshop->getCurrency()}>
<{if $fs.reached}>
<p><{t}>Du har fri fragt<{/t}></p>
<{else}>
<{$fs_amount = $fs.remaining|number_format:2:$fs_decimal:$fs_thousand}>
<p><{t x=$fs_amount y=$fs_currency}>Du mangler {x} {y} for at få fri fragt<{/t}></p>
<{/if}>
<{/if}>
2. Vis bjælken
percent kan bruges direkte som bredde. Den er højst 99, indtil fri fragt er nået, så bjælken aldrig ser fuld ud, mens kunden stadig betaler fragt. Læg bjælken under teksten, inde i null-checket:
<div class="free-shipping__bar" role="progressbar" aria-valuenow="<{$fs.percent}>" aria-valuemin="0" aria-valuemax="100" aria-label="<{t}>Fri fragt<{/t}>">
<div class="free-shipping__fill" style="width: <{$fs.percent}>%"></div>
</div>
Giv den en højde og en baggrund i dit stylesheet. role="progressbar" og aria-attributterne gør, at skærmlæsere også kan fortælle, hvor langt kunden er.
.free-shipping__bar { height: 8px; border-radius: 4px; background: #e8e8e8; overflow: hidden; }
.free-shipping__fill { height: 100%; border-radius: 4px; background: #111827; transition: width .3s; }
3. Hent forslagene
$webshop->getFreeShippingSuggestions($limit) returnerer varer, der giver fri fragt eller bringer kunden tættere på. Standarden er 3, og du kan få op til 12. Metoden læser selv kurven, så du skal ikke give den andet med, og listen er tom, når fri fragt er nået. Hvert element har:
- product: varen, med de samme metoder som i resten af temaet.
- price: prisen for én stk. med samme moms som kurven, som kunden betaler, med kundegruppe, land og valuta, og en kampagne på én vare regnet med.
- reaches: sand, når varen alene giver fri fragt.
- attribute og values: variant-attributten og de varianter, der kan købes. attribute er null, når varen ikke har varianter.
Shoporama vælger varerne i en fast rækkefølge: først varer, der ofte købes sammen med varerne i kurven, derefter de bedst sælgende i samme kategori, og så butikkens bedst sælgende. Giver ingen af dem fri fragt alene, søges der videre i prisklassen blandt varer i kurvvarernes hovedkategori og til sidst i hele butikken, også varer uden salg (kun dem, der giver fri fragt, billigst først). Varer, der alene giver fri fragt, kommer først, og varer, der er langt dyrere end det manglende beløb, bliver sorteret fra. Varer, der allerede er i kurven, udsolgte varer, varer med "Aldrig fri fragt", tilkøb, gavekort, samlepakker, og varer med kundeinput-felter, kommer aldrig med. Du kan altså regne med, at alle forslag kan lægges i kurven direkte fra kortet, varer med varianter efter et valg i rullelisten. Læg forslagene inde i null-checket fra trin 1, fordi de bruger $fs.
<{$fs_suggestions = []}>
<{if !$fs.reached}>
<{$fs_suggestions = $webshop->getFreeShippingSuggestions(3)}>
<{/if}>
<{if $fs_suggestions}>
<p><{t}>Forslag til fri fragt<{/t}></p>
<ul>
<{foreach $fs_suggestions as $fs_item}>
<{$fs_product = $fs_item.product}>
<li>
<a href="<{$fs_product->getRemoteUrl()}>"><{$fs_product->getName()|escape}></a>
<{$fs_item.price|number_format:2:$fs_decimal:$fs_thousand}> <{$fs_currency|escape}>
<{if $fs_item.reaches}><span><{t}>Giver fri fragt<{/t}></span><{/if}>
</li>
<{/foreach}>
</ul>
<{/if}>
4. Byg tilføj-formularen
Hvert forslag er sin egen lille formular, der poster til /, ligesom en almindelig læg i kurv. Den sender product_id, amount, og redirect, så kunden kommer tilbage til samme trin. Har varen varianter, sender du også attributes[attribut-id] med værdiens id. Er der kun én variant at vælge, sender du den som skjult felt. Ellers viser du en rulleliste med required, så kunden ikke kan trykke Tilføj uden at vælge. Erstat <li> fra trin 3 med:
<li>
<form action="/" method="post">
<input type="hidden" name="product_id" value="<{$fs_product->getProductId()}>">
<input type="hidden" name="amount" value="1">
<input type="hidden" name="redirect" value="<{$return|default:'/basket'|escape}>">
<a href="<{$fs_product->getRemoteUrl()}>"><{$fs_product->getName()|escape}></a>
<{$fs_item.price|number_format:2:$fs_decimal:$fs_thousand}> <{$fs_currency|escape}>
<{if $fs_item.reaches}><span><{t}>Giver fri fragt<{/t}></span><{/if}>
<{if $fs_item.attribute}>
<{if $fs_item.values|@count == 1}>
<input type="hidden" name="attributes[<{$fs_item.attribute->getAttributeId()}>]" value="<{$fs_item.values[0]->getAttributeValueId()}>">
<{else}>
<select name="attributes[<{$fs_item.attribute->getAttributeId()}>]" required>
<option value=""><{t}>Vælg<{/t}> <{$fs_item.attribute->getName()|escape}></option>
<{foreach $fs_item.values as $fs_value}>
<option value="<{$fs_value->getAttributeValueId()}>"><{$fs_value->getVal()|escape}></option>
<{/foreach}>
</select>
<{/if}>
<{/if}>
<button type="submit" value="1"><{t}>Tilføj<{/t}></button>
</form>
</li>
values indeholder kun varianter, der kan købes: dem på lager, eller dem alle, hvis varen tillader negativt lager. Er ingen variant købbar, er varen slet ikke med i forslagene. $return sætter du, når du inkluderer komponenten (se næste trin).
5. Placér komponenten
Saml trin 1 til 4 i fx components/free-shipping.html, og inkludér den i kurven (basket.html) og på leveringstrinnet (shipping.html). De to sider caches aldrig, så forslagene kan renderes på serveren og passer altid til kundens kurv.
Det vigtigste er, at komponenten står uden for trinnets egen formular. Kurven og leveringstrinnet er typisk én stor <form>, og HTML tillader ikke formularer inde i formularer. Læg derfor kortet før eller efter den:
<{* basket.html: før kurvens <form> *}>
<{include file="components/free-shipping.html" return="/basket"}>
<{* shipping.html: før trinnets <form>. Wrapperen står der altid *}>
<div id="checkout-free-shipping">
<{include file="components/free-shipping.html" return="/shipping"}>
</div>
Grænsen kan afhænge af fragtmetoden, fordi en fragtmetode kan have sin egen "Fri fragt over". Genindlæser dit tema leveringstrinnet, når kunden vælger en anden fragtmetode, er der intet at gøre. Gemmer temaet valget med AJAX, så skift kortet ud med det nye fra svaret. Derfor står wrapperen der altid, også når kortet er tomt, så kortet kan dukke op, når kunden vælger en metode med en grænse:
// Når kunden vælger en anden fragtmetode: gem valget uden at gå videre
var data = new FormData(form); // leveringstrinnets formular
data.delete('next');
fetch(window.location.pathname, { method: 'POST', body: data, credentials: 'same-origin' })
.then(function(response) { return response.text(); })
.then(function(html) {
var doc = new DOMParser().parseFromString(html, 'text/html');
var fresh = doc.getElementById('checkout-free-shipping');
var current = document.getElementById('checkout-free-shipping');
if (fresh && current) current.replaceWith(fresh);
});
Vil du lade butiksejeren slå bjælken og forslagene til og fra, så brug de samme nøgler som Montana og Delaware. Læg felterne under fields i gruppen checkout_cart (opret gruppen, hvis temaet ikke har den) i theme_settings.json. Uden default er de slået fra, til butiksejeren slår dem til:
{
"path": "enable_free_shipping_bar",
"name": "Vis bjælke for fri fragt",
"description": "Viser i kurven og på leveringstrinnet, hvor meget kunden mangler for at få fri fragt.",
"type": "bool"
},
{
"path": "enable_free_shipping_suggestions",
"name": "Foreslå varer der giver fri fragt",
"description": "Foreslår op til 3 varer, der kan lægges i kurven med ét klik.",
"type": "bool"
}
Inkludér komponenten, når en af dem er slået til, og send dem med som parametre. Inde i komponenten pakker du bjælken ind i <{if $show_bar}> og henter kun forslag, når $show_suggestions er sand:
<{if $settings.checkout_cart.enable_free_shipping_bar || $settings.checkout_cart.enable_free_shipping_suggestions}>
<{include file="components/free-shipping.html" show_bar=$settings.checkout_cart.enable_free_shipping_bar show_suggestions=$settings.checkout_cart.enable_free_shipping_suggestions return="/basket"}>
<{/if}>
6. Undgå faldgruberne
Strengt over. Kassen giver først fri fragt, når kurven er over grænsen. En kurv på præcis 499 kr. med grænsen 499 kr. betaler fragt, og remaining er 0,01. Sammenlign aldrig selv price og limit. Brug reached og remaining, så bjælken altid siger det samme som kassen.
Null-check. $free_shipping_progress kan være null, også når kurven har varer, fx når en af dem har "Aldrig fri fragt". Tjek den, før du læser felterne, og vis intet, når den er null.
B2B, inkl. og ekskl. moms. Grænsen måles på kurvens pris med moms, ligesom i kassen, og det samme gælder price i forslagene. Handler kunden uden moms, fx en erhvervskunde med momsnummer, er $vat 0, og så måles beløbet uden moms. Viser dit tema priser ekskl. moms, så regn kun visningen om, og skriv kun "(inkl. moms)" ved det manglende beløb, når kunden betaler moms. Sådan gør MontanaB2B, lidt forenklet:
<{* B2B: varens pris ekskl. moms til visning *}>
<{$fs_price = $fs_item.price}>
<{if $b2b}>
<{$fs_vat_factor = 1 + ($fs_product->getProductVat() / 100)}>
<{if $fs_vat_factor > 0}><{$fs_price = $fs_price / $fs_vat_factor}><{/if}>
<{/if}>
<{$fs_price|number_format:2:$fs_decimal:$fs_thousand}> <{$fs_currency|escape}>
<{* Det manglende beløb er inkl. moms, som i kassen. Uden moms ($vat 0) måles det uden moms *}>
<{t x=$fs_amount y=$fs_currency}>Du mangler {x} {y} for at få fri fragt<{/t}><{if $b2b && $vat > 0}> <{t}>(inkl. moms)<{/t}><{/if}>
Egen formular pr. vare. Giv hvert forslag sin egen formular, og læg aldrig komponenten inde i kurvens eller leveringstrinnets formular. Ellers sender knappen den forkerte formular.
Server side tracking. Når butikken bruger server side tracking, fulgte kunden tidligere altid indstillingen "Når kunden har lagt en vare i kurven skal de gå til:" og så bort fra redirect. Nu kommer kunden tilbage til /basket eller /shipping, når redirect er en af dem, med sporingens parametre i adressen. Dem skal temaet ikke gøre noget ved. Andre værdier af redirect virker som før.
De ældre variabler. $free_shipping_limit og $free_shipping_price findes stadig og virker som før, men de tager ikke højde for gavekort, brugte point, varer med "Aldrig fri fragt", eller fri fragt fra en rabatkode eller loyalitetsprogrammet. Byg nye temaer på $free_shipping_progress.
Læs mere
Den samlede komponent, alle felterne, og reglerne står i udviklerdokumentationen under Fri fragt: bjælke og forslag. Vil du se det i et rigtigt tema, er Montana referencen. Hvordan butiksejeren slår det til i standardtemaerne, og hvordan forslagene vælges, står i supportartiklen om fri fragt-bjælken og forslagene. Har du spørgsmål, så skriv til support@shoporama.dk.
Relaterede artikler
Fri fragt-bjælke og forslag til varer i kurven
Vis kunden, hvor meget der mangler for at få fri fragt, med en bjælke i kurven og på leveringstrinnet, og foreslå varer, der giver fri fragt, med...
Fri fragt over et bestemt beløb
Sådan opsætter du fri fragt over et bestemt beløb i Shoporama. Grænsen sættes pr. land under Levering → Lande eller på den enkelte fragtmetode. Du...
Tilkøb i kurv og checkout
Sådan tilbyder du gaveindpakning, hilsenkort, opbæring og andre tilkøb med ét flueben i kurven og på sidste trin før betaling. Opret en...
Flere fra bloggen
Adresseforslag i kassen: kunden vælger adressen, og postnummer og by udfylder sig selv
Nu kan seks af de indbyggede temaer foreslå danske adresser, mens kunden skriver. Kunden vælger sin adresse fra Danmarks Adresseregister, og...
10. november 2026
Nu kan Claude ændre dit tema, og du ser det, før kunderne gør
Bed Claude om et "Kun 3 tilbage" på de sidste varer, en nedtælling til Black Week, eller et juletema, du slår fra igen i januar. Claude bygger det...
07. november 2026
Ordretjek markerer forkerte postnumre og mulig kortsvindel, før du sender
Et forkert postnummer kan koste et gebyr hos fragtfirmaet, og en stribe næsten ens ordrer på kort tid kan være nogen, der tester stjålne kort. Nu...
06. november 2026