Documentation

Customizing Content

Backend Content

  • Products
  • Categories
  • Media

Frontend Content

  • Text
  • UI Components

Nuxt Content

To manage text and UI components, we use Nuxt Content. Official Nuxt Content Documentation

Structure

These are the default CMS pages that are already available:

content/
├── index.yaml
├── navigation.yaml
├── impressum.md
└── unternehmen/
    ├── agb.md
    ├── datenschutz.md
    └── zahlung-und-versand.md

You can of course create more and/or override these. You probably want to override the content of your homepage. To do this, proceed as follows:

  1. Create the mandatory content.config.ts file with the content:
export { default } from '@shopbite-de/storefront/content.config'
export * from '@shopbite-de/storefront/content.config'
  1. Create a new file in your content folder. For example /content/index.yml
  2. Look at the original file.
  3. Copy the content into your new file and override the content

You can of course also completely override the content configuration including the data objects and create your own content and structures if you want to redesign your homepage from scratch.

If you want to create additional landing pages, you can do so. By creating a Markdown file in your content folder. The file can be located anywhere in the folder. The file structure is also reflected in the URL path.

For example:

  • content/meine-landing-page.md --> https://shop.de/meine-landing-page
  • content/unterseite/landing-page-2.md --> https://shop.de/unterseite/unterseiten-landing-page-2

UI Components (Nuxt UI)

You can also use UI components in the Markdown files. You can basically use all UI components from Nuxt UI.

For more information, including styling, see the Official Documentation

Media and performance

The home page loads the hero video and the images from content/index.yml first. Oversized files make the biggest difference in Lighthouse ("Performance", metric LCP), especially on phones.

Hero video and poster

  • Add a poster for the video (hero.poster): the first frame as WebP, 1280 px wide, already blurred (boxblur, the video gets the same blur via CSS), about 20 to 30 KB. The storefront renders the poster as the first image of the page and preloads it. Phones (below 768 px) never load the video, only the poster.
  • The video itself: 720p, no audio, 5 to 10 seconds, under 1 MB. A 3 MB 1080p video doubles the load time of the home page.

Create both with ffmpeg:

ffmpeg -ss 1 -i hero.mp4 -frames:v 1 -vf "scale=1280:-2,boxblur=4:2" -c:v libwebp -quality 70 hero-poster.webp
ffmpeg -i hero.mp4 -vf "scale=1280:-2" -r 25 -c:v libx264 -crf 28 -preset medium -an -movflags +faststart hero-720p.mp4
hero:
  backgroundVideo: https://shop.example.com/media/.../hero-720p.mp4
  poster: /hero-poster.webp

The poster can live in the public/ folder of the shop project; the video belongs in the Shopware media.

Hero copy and buttons

The hero shows the USPs (hero.usps) as a compact row at the bottom left, above them the title, the description, a status line and the buttons:

  • hero.title (optional): the title, e.g. "Italian, freshly delivered." Without it, the shop name (title) is used.
  • hero.headline (optional): small line above the title, e.g. the restaurant name in capitals.
  • The status line ("Geöffnet bis 23:00 · Lieferung ca. 30 Min") comes automatically from the business hours and the plugin's delivery time.
  • The first entry in hero.links is the single order button; all further entries render as quiet text buttons.

Directly below the hero, the home page shows the top sellers (products with "Mark as top seller" in Shopware, up to eight) as a horizontal row; a tap opens the product options.

hero:
  title: Italienisch, frisch geliefert.
  headline: ALTE SCHMIEDE

Information cards

The section "Alle Informationen auf einen Blick" renders the entries of features.features as cards: icon, small category (title), a bold lead line, detail text (description) and an optional action.

  • kind (optional): hours fills the lead with today's opening hours ("Heute 11:30–14:30 und 17:30–23:00", "Heute Ruhetag") plus the open/closed status, delivery with the delivery time from the plugin ("In ca. 30 Minuten bei dir"). Both are live, the text needs no maintenance.
  • lead (optional): the lead line for every other card, e.g. "Tisch nur telefonisch".
  • link (optional): label and to. A tel: target becomes a call button, anything else a text link. The opening hours in the footer are reachable via #oeffnungszeiten.
features:
  features:
    - title: Öffnungszeiten
      kind: hours
      description: Samstags 17:30 - 23:30 Uhr, Dienstag ist Ruhetag.
      icon: i-lucide-clock
      link:
        label: Alle Öffnungszeiten
        to: "#oeffnungszeiten"
    - title: Reservierung
      kind: reservation
      lead: Tisch nur telefonisch
      description: Bestellungen nehmen wir gerne online oder telefonisch entgegen.
      icon: i-lucide-phone
      link:
        label: 06104 71427 anrufen
        to: "tel:+49610471427"

Images

  • Category and product images from Shopware need no extra work: Shopware generates thumbnails (400, 800 and 1920 px), the storefront serves them as srcset, the browser loads the fitting size.
  • Images from content/index.yml (CTA background, gallery, customer photos) are loaded as they are. Use WebP up to 1920 px wide, under 200 KB. If the image lives in the Shopware media, use the thumbnail URL (/thumbnail/.../name_1920x545.webp) instead of the original.
  • The gallery shows images in 4:3 and loads them on scroll.

Cache headers on the object storage

If the Shopware media lives on S3 storage (e.g. Hetzner Object Storage), the storage sends no Cache-Control header by default. Every visit reloads all images and the video. Shopware's media URLs carry a timestamp, so they may be cached for a long time:

# all existing objects (AWS CLI with the Hetzner endpoint)
aws --endpoint-url https://nbg1.your-objectstorage.com s3 cp s3://my-bucket/ s3://my-bucket/ \
  --recursive --metadata-directive REPLACE --cache-control "public, max-age=31536000, immutable"

For new uploads set the header in Shopware: shopware.filesystem.public.config.options.CacheControl: "public, max-age=31536000, immutable" in config/packages/shopware.yaml. Check the result with curl -I <media URL>: the response must contain a cache-control line.

Checking

npx lighthouse@12 https://www.my-shop.com/ --only-categories=performance --view

Target: performance above 90 and LCP below 2.5 seconds on a phone.