# Bestellhistorie UI: Liste, Details und Aktionen

> Bestellhistorie UI für React: Bestellungen nach Monat, Suche und Statusfilter, Tracking, Nachkauf und Rechnung pro Zeile und Details, die aufs Handy passen.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 6. Nov. 2026
- Kategorie: [Praxis: Screens und Apps](https://wingo-ui.com/de/blog/category/build)
- Lesezeit: 9 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/order-history-page-design
- Englische Fassung: https://wingo-ui.com/blog/order-history-page-design

## Kurz gesagt

Eine gute Bestellhistorie UI beantwortet vier Fragen ohne zusätzliche Klicks: Wo ist meine Bestellung, kann ich sie zurückschicken, was habe ich gekauft und wo ist die Rechnung? Gruppier die Bestellungen nach Monat, ergänze Suche und Statusfilter, setz Track, Buy again und Invoice in jede Zeile und zeig die ganze Bestellung auf dem Desktop neben der Liste und auf dem Smartphone als eigenen Screen. Der Block Order History Page von Wingo UI erledigt all das; du bringst deine Bestellungen in seine Form und verbindest drei Callbacks.

Eine Bestellhistorie wirkt wie ein gelöstes Problem: eine Tabelle mit Datum, Summe und einem View-Link. Dann füllt sich das Support-Postfach mit „Wo ist meine Bestellung?“ und „Kann ich das noch zurückschicken?“, weil die Tabelle genau diese Fragen nie beantwortet hat. Dieser Guide zeigt, was eine Bestellhistorie UI von sich aus leisten muss. Als Beispiel dient der Block [Order History Page](https://wingo-ui.com/components/order-history-page) von Wingo UI, der Bibliothek, die wir bauen: Bestellungen nach Monat gruppiert, Suche und Statusfilter, Track, Buy again und Invoice in jeder Zeile und Details, die auf dem Desktop neben der Liste stehen und auf dem Smartphone zu einem eigenen Screen werden.

## Warum öffnen Leute ihre Bestellhistorie?

Die Hälfte will eine offene Bestellung verfolgen, der Rest sucht Retouren, Belege und alte Käufe. Die [Recherche des Baymard Institute zur Bestellübersicht](https://www.baymard.com/ecommerce-design-examples/62-orders-overview) (abgerufen am 6. November 2026) nennt die Gründe für einen Besuch der Seite: 50% verfolgen eine offene Bestellung, 26% starten eine Retoure, 26% sehen sich frühere Bestellungen an, 19% ändern eine laufende Bestellung, 15% stornieren und 8% laden eine Rechnung oder einen Beleg herunter. Die [Studie 2026 zu Kundenkonten und Self-Service](https://baymard.com/blog/accounts-and-self-service-ux-research-2026) (veröffentlicht am 9. September 2026) ergänzt zwei Ergebnisse, die das Layout prägen: Bestelldetails in eingeklappten Bereichen schnitten im Test so schlecht ab wie gar keine Details, und die Teilnehmenden gingen zu früheren Bestellungen zurück, um dieselben Artikel noch einmal zu kaufen, statt den Shop zu durchsuchen.

Damit hat die Bestellhistorie eines Shops vier Aufgaben, und jede hat ihren Platz auf dem Screen:

| Frage | Wo die Antwort steht |
|---|---|
| Wo ist meine Bestellung? | Ein Status-Badge in der Zeile, Track, solange sie unterwegs ist, der Tracker oben in den Details |
| Kann ich das zurückschicken? | Return items in den Details bis zum Rückgabedatum, danach das Datum, an dem die Frist endete |
| Was habe ich gekauft? | Artikelfotos in der Zeile, Artikel mit Varianten, Mengen und Preisen in den Details |
| Wo ist die Rechnung? | Invoice in der Zeile und in den Details |

Fertige Blöcke für eine Order History UI gibt es. [Tailwind Plus](https://tailwindcss.com/plus/ui-blocks/ecommerce/page-examples/order-history-pages) hat eine eigene Kategorie für Order History Pages, und [shadcnblocks](https://www.shadcnblocks.com/blocks/order-history) führt fünf Order-History-Blöcke mit Status-Tabs, Filtern, Status pro Artikel, einer Timeline und Retouren (beide geprüft am 6. November 2026). Die Beschreibungen bei shadcnblocks erwähnen weder eine Suche noch Details neben der Liste oder eine eigene Ansicht fürs Smartphone. Prüf also, was du noch selbst bauen müsstest: Suche, Liste und Details auf einem Screen, die Navigation auf dem Smartphone und den Ladezustand jeder Aktion.

## Wie sieht eine gute Bestellhistorie UI aus?

Eine Liste, die sich schnell überfliegen lässt, und Details, mit denen man direkt etwas erledigen kann. Installier den Block, um mitzumachen:

```bash
npx wingo-ui@latest add order-history-page
```

Die Liste gruppiert Bestellungen unter Monatsüberschriften (`groupBy="month"`, oder `"none"` für eine flache Liste) und sortiert sie nach `placedAt`, die neueste zuerst, egal in welcher Reihenfolge du sie übergibst. Jede Zeile zeigt die Bestellnummer, ein Status-Badge, das Datum, die Zahl der Artikel, die Summe und die Artikelfotos (drei plus einen Zähler +N, wenn eine Bestellung mehr als vier Positionen hat). Die Aktionen richten sich nach dem Status: Track, solange eine Bestellung in Bearbeitung oder versandt ist, Buy again, sobald sie zugestellt, retourniert oder storniert ist, und Invoice bei allen Bestellungen außer den stornierten.

Über der Liste sitzen ein Suchfeld und ein Statusfilter. Die Suche findet Bestellnummern (mit oder ohne `#`), Produktnamen, Varianten und SKUs: „skyline“ findet den Print, „NW-PR“ jeden Print. Der Filter bietet nur die Status an, die in der Liste vorkommen, so wählt niemand Returned und bekommt eine leere Liste. Drück irgendwo `/`, um in die Suche zu springen (`searchShortcut`). Passt nichts, bietet das leere Ergebnis Clear filters an.

> Live-Demo (Order History Page): Tipp „print“ ins Suchfeld und wähl dann im Filter Delivered. Öffne die Bestellung W-10498, um das Rückgabefenster zu sehen, und W-10352, um zu sehen, wie sich eine abgelaufene Frist liest. Zieh das Fenster schmaler als 768px, dann wird die Liste zu Karten, die ihren eigenen Screen öffnen. Probier sie unter [Order History Page](https://wingo-ui.com/components/order-history-page) aus und installier sie mit `npx wingo-ui@latest add order-history-page`.

## Was gehört in die Bestelldetails?

Alles, und zwar ausgeklappt. Baymards Ergebnis zu eingeklappten Bereichen ist der Grund, warum die Details jeden Teil offen zeigen, in der Reihenfolge, in der Leute danach suchen:

1. **Der Tracker.** Status, Lieferfenster oder Zustellzeit, Meilensteine, der Paketdienst mit einer Sendungsnummer zum Kopieren und der Sendungsverlauf. Das ist der [Order Tracker](https://wingo-ui.com/components/order-tracker), so beantwortet die Seite die Frage „Wo ist mein Paket?“, ohne die Leute auf die Website des Paketdiensts zu schicken.
2. **Artikel.** Foto, Titel, Variante, Menge, Stückpreis und Zeilensumme.
3. **Versand und Zahlung.** Liefer- und Rechnungsadresse („Same as shipping address“, wenn beide gleich sind), die Zahlungsart („Visa ending in 4242“) und die Versandart, angeordnet mit der [Description List](https://wingo-ui.com/components/description-list).
4. **Übersicht.** Zwischensumme, Rabatt mit Gutscheincode, Versand (Free, wenn er 0 beträgt), Steuer und Summe.
5. **Hilfe.** Return items, solange die Frist läuft, das Enddatum, sobald sie abgelaufen ist, und Get help.

Die Rückgabezeile verdient einen eigenen Hinweis. Laut derselben Baymard-Studie sorgte es für unnötige Unsicherheit und kostete die Teilnehmenden Zeit, wenn der Button Start Return nach Ablauf der Frist einfach verschwand. Der Block lässt an seiner Stelle einen Satz stehen: „The return window closed on Oct 12, 2026.“ Wer das liest, hört auf zu suchen, und genau darum geht es.

Der Tracker funktioniert auch allein, zum Beispiel auf der Bestellseite, auf die deine Versand-E-Mail verlinkt:

```tsx
import { OrderTracker } from "@/components/ui/order-tracker";

export function ShipmentStatus() {
  return (
    <OrderTracker
      orderNumber="#W-10531"
      status="shipped"
      placedAt="2026-11-04T19:42:00"
      eta={{ from: "2026-11-09T09:00:00", to: "2026-11-09T18:00:00" }}
      courier={{
        name: "UPS Ground",
        trackingNumber: "1Z 999 AA1 01 2345 6784",
        trackingUrl: "https://www.ups.com/track?tracknum=1Z999AA10123456784",
      }}
      events={[
        { id: "e2", title: "Versandt", location: "Secaucus, NJ", time: "2026-11-05T16:10:00", status: "shipped" },
        { id: "e1", title: "Bestellung aufgegeben", time: "2026-11-04T19:42:00", status: "placed" },
      ]}
      currency="USD"
      locale="en-US"
    />
  );
}
```

> Live-Demo (Order tracker): Kopier die Bestell- oder die Sendungsnummer und drück dann Show full history, um die älteren Ereignisse nacheinander aufzuklappen. Probier sie unter [Order tracker](https://wingo-ui.com/components/order-tracker) aus und installier sie mit `npx wingo-ui@latest add order-tracker`.

## Wie funktioniert die Seite „Meine Bestellungen“ auf dem Smartphone?

Als zwei Screens: zuerst die Liste, dann die Bestellung in einer eigenen Ansicht. Auf einem breiten Bildschirm öffnen sich die Details neben der Liste, die offene Zeile hebt sich ab, und du kannst die Trennlinie ziehen, um die Liste zwischen 320 und 560px breit zu machen (`listWidth` legt den Startwert fest). Unter 768px werden die Zeilen zu Karten, und ein Tipp schiebt die Bestellung als eigene Ansicht herein, mit dem Zurück-Button Orders. Wisch auf den Details nach rechts oder drück Escape, um zurückzugehen. Die Liste bleibt darunter gemountet und behält so ihre Scrollposition.

Das Layout stammt aus der [Split View](https://wingo-ui.com/components/split-view), die du für jeden Screen mit Liste und Details nutzen kannst. Der Breakpoint von 768px bezieht sich auf die Breite des Blocks selbst. Das gestapelte Layout greift also auch, wenn die Seite in einer schmalen Spalte neben einer Konto-Sidebar sitzt.

Auf dem Smartphone zählen zwei Details. Erstens startet ein Smartphone immer mit der Liste, auch wenn eine Bestellung ausgewählt ist. Öffnen sich lange Details, bevor jemand eine Bestellung gewählt hat, verdecken sie genau die Liste, für die er gekommen ist. Zweitens öffnet sich der Statusfilter auf Viewports unter 768px als Bottom Sheet statt als Dropdown, so liegen die Optionen unter dem Daumen. Wer auf dem Desktop mit der Tastatur arbeitet, wechselt mit Pfeil nach oben und unten zwischen den Bestellungen, springt mit Home und End zur ersten und letzten Bestellung und öffnet mit Enter.

Wenn deine Bestellungen heute noch breite Tabellen sind: Unser Beitrag zu [responsiven Tabellen als mobile Karten](https://wingo-ui.com/de/blog/responsive-table-mobile-cards) zeigt denselben Wechsel für andere Daten.

## Wie binde ich die Bestellhistorie in Next.js ein?

Lad die Bestellungen auf dem Server, bring sie in die Form `OrderHistoryOrder` des Blocks und render den Block in einer Client-Komponente mit drei Callbacks. Zuerst das Mapping, denn dort steckt der eine Stolperstein: Der Block nimmt Beträge in Haupteinheiten (Dollar, nicht Cent), und Zahlungsanbieter wie Stripe speichern Cent.

```ts
// app/account/orders/map.ts
import type { OrderHistoryOrder, OrderHistoryStatus } from "@/components/blocks/order-history-page-data";
import type { OrderRow } from "@/lib/shop";

const STATUS: Record<OrderRow["state"], OrderHistoryStatus> = {
  pending: "processing",
  paid: "processing",
  fulfilled: "shipped",
  delivered: "delivered",
  refunded: "returned",
  canceled: "cancelled",
};

// in Cent gespeichert, in Dollar angezeigt
const dollars = (cents: number) => cents / 100;

export function toOrderHistoryOrder(row: OrderRow): OrderHistoryOrder {
  return {
    id: row.id,
    number: row.number,
    placedAt: row.createdAt,
    status: STATUS[row.state],
    items: row.lines.map((line) => ({
      id: line.id,
      title: line.productName,
      variant: line.variantName ?? undefined,
      image: line.imageUrl ?? undefined,
      quantity: line.quantity,
      price: dollars(line.unitPriceCents),
      sku: line.sku,
    })),
    shipping: dollars(row.shippingCents),
    shippingMethod: row.shippingMethod,
    discount: row.discountCents ? dollars(row.discountCents) : undefined,
    discountCode: row.discountCode ?? undefined,
    tax: dollars(row.taxCents),
    total: dollars(row.totalCents),
    shippingAddress: row.shippingAddress,
    payment: row.card ? { method: row.card.brand, last4: row.card.last4 } : undefined,
    eta: row.estimatedDelivery ? { from: row.estimatedDelivery } : undefined,
    deliveredAt: row.deliveredAt ?? undefined,
    courier: row.trackingNumber
      ? { name: row.carrier, trackingNumber: row.trackingNumber, trackingUrl: row.trackingUrl ?? undefined }
      : undefined,
    returnBy: row.returnBy ?? undefined,
  };
}
```

`OrderRow` und `getOrdersForCustomer` stehen für deine eigene Datenschicht. Lass `billingAddress` weg, wenn sie der Lieferadresse entspricht, dann weisen die Details selbst darauf hin. Die Seite bleibt eine Server-Komponente; in Next.js 16 ist `searchParams` ein Promise:

```tsx
// app/account/orders/page.tsx
import { getOrdersForCustomer } from "@/lib/shop";
import { AccountOrders } from "./account-orders";
import { toOrderHistoryOrder } from "./map";

export default async function OrdersPage({ searchParams }: { searchParams: Promise<{ order?: string | string[] }> }) {
  const [{ order }, rows] = await Promise.all([searchParams, getOrdersForCustomer()]);
  const initialOrder = typeof order === "string" ? order : undefined;
  return <AccountOrders orders={rows.map(toOrderHistoryOrder)} initialOrder={initialOrder} />;
}
```

Die Client-Komponente verbindet die Aktionen. Jeder Callback darf ein Promise zurückgeben: Der Button zeigt einen Spinner, solange es läuft, und danach einen Toast (der Block mountet seinen eigenen Toaster). Ein geworfener Fehler zeigt seine Meldung in einem Toast, oder `labels.actionFailed` („Something went wrong. Try again.“), wenn er keine hat. Antworte auf Fehler also mit einem Text, mit dem Käufer etwas anfangen können.

```tsx
// app/account/orders/account-orders.tsx
"use client";

import { useRouter } from "next/navigation";
import { OrderHistoryPage } from "@/components/blocks/order-history-page";
import type { OrderHistoryOrder } from "@/components/blocks/order-history-page-data";

async function post(url: string, body?: unknown) {
  const response = await fetch(url, {
    method: "POST",
    headers: body ? { "Content-Type": "application/json" } : undefined,
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!response.ok) throw new Error(await response.text());
}

export function AccountOrders({ orders, initialOrder }: { orders: OrderHistoryOrder[]; initialOrder?: string }) {
  const router = useRouter();
  return (
    <OrderHistoryPage
      orders={orders}
      currency="USD"
      locale="en-US"
      height="fill"
      defaultSelectedId={initialOrder}
      // die offene Bestellung steht in der URL, so öffnen ein Neuladen oder ein geteilter Link sie auf dem Desktop wieder
      onSelect={(id) =>
        window.history.replaceState(null, "", id ? `?order=${encodeURIComponent(id)}` : window.location.pathname)
      }
      onReorder={(order) => post("/api/cart/lines", { items: order.items.map(({ sku, quantity }) => ({ sku, quantity })) })}
      onReturn={async (order) => {
        await post(`/api/orders/${order.id}/returns`);
        router.refresh();
      }}
      onDownloadInvoice={(order) => {
        window.open(`/api/orders/${order.id}/invoice.pdf`, "_blank");
      }}
      helpHref="/support"
      shopHref="/shop"
    />
  );
}
```

Ein paar Regeln für die Serverseite:

- **Buy again berechnet die Preise neu.** Der Standard-Toast sagt „Prices and stock are checked again at checkout.“ Sorg dafür, dass das stimmt: Leg die SKUs in den Warenkorb und lass den Warenkorb die heutigen Preise nachschlagen, nie die alten aus der Bestellung.
- **Prüf die Rückgabefrist noch einmal.** Der Block bietet Return items bis `returnBy` nach der Uhr des Clients an (`now` überschreibt sie). Die echte Entscheidung trifft dein Endpunkt für Retouren.
- **Entscheide dich für einen Rechnungsweg.** Mit `onDownloadInvoice` erscheint Invoice bei jeder Bestellung, die nicht storniert wurde, also muss dein Endpunkt für alle eine Antwort haben. Ohne den Callback ist Invoice ein Download-Link auf die `invoiceUrl` der Bestellung und fehlt, wenn eine Bestellung keine hat.

`height="fill"` lässt den Block die restliche Höhe einer Flex-Spalte einnehmen, genau richtig in einem Konto-Layout mit eigenem Header. Auf einer eigenständigen Seite macht ihn der Standardwert `height="screen"` exakt so hoch wie den Viewport. Mehr braucht die Seite Meine Bestellungen in React nicht: eine Server-Seite, eine Mapping-Funktion und eine Client-Komponente.

## Wie ändere ich Texte und Aussehen?

Über Props. Jeder sichtbare Text steckt in `labels`, mit englischen Standardwerten in `DEFAULT_ORDER_HISTORY_PAGE_LABELS`, und `statuses` wird Eintrag für Eintrag zusammengeführt. Du kannst also einen Status umbenennen und die übrigen behalten:

```tsx
<OrderHistoryPage
  orders={orders}
  labels={{
    title: "Deine Bestellungen",
    description: "Verfolge, retourniere oder bestell alles noch einmal, was du gekauft hast.",
    statuses: { processing: "In Vorbereitung" },
    returnUntil: (date) => `Kostenlose Rücksendung bis ${date}.`,
  }}
  tone="brand"
  radius="lg"
  showMap
/>
```

`tone` färbt den Fortschritt im Tracker und den Button Buy again (standardmäßig neutral), `radius` setzt die Panels auf 12, 16 oder 24px, `showMap` ergänzt den Tracker um eine Illustration der Route, und `showHistory={false}` blendet den Sendungsverlauf aus. Gib `loading` mit, um Skeleton-Zeilen zu zeigen, solange die Bestellungen laden. Ein leeres Array in `orders` zeigt „No orders yet“ mit einem Link Start shopping.

## Was sollte ich daneben bauen?

Die Seiten, die ihr die Bestellungen liefern. Bestellungen entstehen im Checkout, und der Block [Checkout Page](https://wingo-ui.com/components/checkout-page) endet mit einer Bestätigung samt Track order. Genau dort sollte diese Seite aufgehen. Unser [Tutorial zur React Checkout Page](https://wingo-ui.com/de/blog/react-checkout-page) baut ihn ein, und die Vorlage [Storefront Page](https://wingo-ui.com/templates/storefront-page) deckt den Shop davor ab. Für die übrigen Screens einer App lies die [Praxis-Guides](https://wingo-ui.com/de/blog/category/build), angefangen mit dem [Guide zu SaaS Website Templates](https://wingo-ui.com/de/blog/saas-website-template).

## Wo fange ich an?

Öffne die [Doku zur Order History Page](https://wingo-ui.com/components/order-history-page): Im Playground wechselst du Startstatus und Bestellung, änderst Tone, Radius und Listenbreite und probierst den Ladezustand aus. Der Block, der Tracker und die Split View gehören zu Wingo UI Pro, das Stand November 2026 $8 im Monat, $80 im Jahr oder einmalig $150 kostet ([Preise](https://wingo-ui.com/de/pricing)). Die Erstattung innerhalb von 14 Tagen gilt nur, solange du noch kein Pro-Item heruntergeladen hast. Probier also zuerst alles im Playground aus.

## Komponenten in diesem Artikel

- [Order History Page](https://wingo-ui.com/components/order-history-page) (Pro): A React order history page for an ecommerce account: orders grouped by month with search and filters, and a full order detail beside them. Installation: `npx wingo-ui@latest add order-history-page`
- [Order tracker](https://wingo-ui.com/components/order-tracker) (Pro): A React order tracker: status, arrival window, milestones, a courier route, the tracking number, shipment history and the order total. Installation: `npx wingo-ui@latest add order-tracker`
- [Checkout Page](https://wingo-ui.com/components/checkout-page) (Pro): A React checkout page from the cart to Order placed: an editable cart, four checkout steps and the order confirmation. Installation: `npx wingo-ui@latest add checkout-page`
- [Split View](https://wingo-ui.com/components/split-view) (Pro): A React split view: a list and its detail side by side on wide screens, a push and pop stack with swipe back on phones. Installation: `npx wingo-ui@latest add split-view`
- [Description List](https://wingo-ui.com/components/description-list) (Pro): A React description list for detail pages: key-value grids for clients, invoices and profiles, with copy buttons, skeletons and iOS rows. Installation: `npx wingo-ui@latest add description-list`

## Häufige Fragen

### Was sollte eine Bestellhistorie zu jeder Bestellung zeigen?

Die Bestellnummer, das Datum, ein Status-Badge, die Zahl der Artikel, die Summe und kleine Fotos der Artikel, dazu die Aktionen, die zum Status passen: Track, solange sie unterwegs ist, Buy again, sobald sie abgeschlossen ist, und Invoice, außer sie wurde storniert. Eine Bestellung sollte man erkennen, ohne sie zu öffnen.

### Sollten Bestelldetails auf einer eigenen Seite oder neben der Liste aufgehen?

Auf breiten Bildschirmen neben der Liste, damit man Bestellungen vergleichen kann, ohne die Stelle in der Liste zu verlieren, und auf dem Smartphone als eigener Screen mit Zurück-Button und Wischgeste. Ein Modal passt hier schlecht, weil die Details lang sind und man darin scrollt.

### Wie zeige ich eine abgelaufene Rückgabefrist an?

Lass in den Bestelldetails eine Zeile stehen, die sagt, wann die Frist endete, statt den Rückgabe-Button stillschweigend zu entfernen. Laut Baymards Studie 2026 zu Kundenkonten sorgte es für unnötige Unsicherheit und kostete die Teilnehmenden Zeit, wenn der Button Start Return nach Ablauf der Frist einfach verschwand.

### Nimmt der Block für die Bestellhistorie Preise in Cent oder in Dollar?

In Dollar. Der Block von Wingo UI nimmt jeden Betrag in Haupteinheiten, also teilst du die Cent-Beträge, die Stripe und die meisten Datenbanken speichern, beim Mapping deiner Zeilen durch 100.

### Braucht die Suche auf einer Seite Meine Bestellungen einen Server-Endpunkt?

Nicht, wenn du die Bestellungen eines Kunden in einer Antwort laden kannst: Der Block von Wingo UI filtert auf dem Client nach Bestellnummer, Produktname, Variante und SKU. Konten mit Hunderten von Bestellungen, etwa von B2B-Käufern, brauchen stattdessen Suche und Paging auf dem Server.

---

Quelle: https://wingo-ui.com/de/blog/order-history-page-design
