---
name: order-tracking
title: Order Tracking
url: https://ui.frontic.com/components/sections/order-tracking.md
tier: sections
install: npx @frontic/ui add order-tracking
files:
  - components/sections/order-tracking/OrderTracking.vue
  - components/sections/order-tracking/index.ts
  - components/sections/order-tracking/types.ts
registryDependencies:
  - badge
  - button
  - card
  - format
  - stepper
  - tooltip
dependencies:
  - "@lucide/vue"
  - reka-ui
---

# Order Tracking

Order status card led by the arrival estimate, with the delivery steps as a timeline, each with its date and a one-line note, the carrier's tracking number with a copy action, and track and reschedule actions.

## Install

```sh
npx @frontic/ui add order-tracking
```

Built from: [Badge](https://ui.frontic.com/components/ui/badge.md), [Button](https://ui.frontic.com/components/ui/button.md), [Card](https://ui.frontic.com/components/ui/card.md), [Format](https://ui.frontic.com/lib/format.md), [Stepper](https://ui.frontic.com/components/ui/stepper.md), [Tooltip](https://ui.frontic.com/components/ui/tooltip.md)

npm packages: `@lucide/vue`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { OrderTracking, type TrackingStep } from '@/components/sections/order-tracking'

const steps: TrackingStep[] = [
  { title: 'Ordered', date: '2026-10-02', note: 'Payment confirmed' },
  { title: 'Packed', date: '2026-10-03', note: 'Packed at our warehouse in Munich' },
  { title: 'Shipped', date: '2026-10-05', note: 'Left the sorting centre this morning · next stop your local depot' },
  { title: 'Delivered', note: 'Expected by Oct 9' },
]
</script>

<template>
  <div class="mx-auto w-full max-w-md">
    <OrderTracking
      title="Order #FR-20377"
      description="AlpinTech Navigator 45 · placed Oct 2"
      status="In transit"
      :steps="steps"
      :current="3"
      eta="Arriving Friday, Oct 9"
      eta-detail="by 14:00"
      carrier="DHL Parcel"
      tracking-number="00340434161094042557"
      tracking-url="https://www.dhl.de/en/privatkunden/pakete-empfangen/verfolgen.html?piececode=00340434161094042557"
      track-label="Track on DHL"
    />
  </div>
</template>
```

## API reference

### OrderTracking

Shows when a parcel arrives and its delivery steps as a timeline, with the tracking number and link.

Styling: [`meta`](https://ui.frontic.com/DESIGN.md#meta); [`line-title`](https://ui.frontic.com/DESIGN.md#line-title). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `string` |  | The card's heading, e.g. the order's reference. |
| `description` | `string` |  | Shown under the heading. |
| `status` | `string` |  | Shown on the badge beside the heading, e.g. `In transit`. |
| `steps` (required) | `TrackingStep[]` |  | The parcel's stages from order to delivery, one stepper item each. |
| `current` (required) | `number` |  | The step the parcel is at, 1-based; earlier steps show as done. |
| `eta` | `string` |  | The expected delivery, the card's headline, e.g. `Arrives Thursday`. |
| `etaDetail` | `string` |  | Shown beside the expected delivery, e.g. the time window. |
| `carrier` | `string` |  | The shipping company, shown before the tracking number's label. |
| `trackingNumber` | `string` |  | The parcel's tracking number, with a button that copies it; the parcel row hides when left out. |
| `trackingUrl` | `string` |  | The carrier's tracking page, opened in a new tab by the track button; no button when left out. |
| `trackingNumberLabel` | `string` | `'tracking number'` | Shown above the tracking number. |
| `trackLabel` | `string` | `'Track parcel'` | Shown on the button that opens the carrier's tracking page. |
| `changeDateLabel` | `string` | `'Change delivery date'` | Shown on the button that asks to change the delivery date. |
| `copyLabel` | `string` | `'Copy tracking number'` | Screen-reader name and tooltip of the button that copies the tracking number. |
| `copiedLabel` | `string` | `'Copied'` | Shown in the copy button's tooltip once the number is copied. |
| `surface` | `'raised' \| 'bare'` | `'raised'` | Bare drops the edge, shadow and inset, for a section placed inside another Card. |
| `locale` | `string` |  | BCP 47 locale the step dates are formatted in; the ConfigProvider's when left out. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `copy` | `trackingNumber: string` | Fires with the tracking number after the shopper copies it to the clipboard. |
| `changeDate` |  | Fires when the shopper asks to change the delivery date. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-surface` | `'raised' \| 'bare'` | Carries the `surface` prop. |

## Exports

```ts
import { type TrackingStep } from '@/components/sections/order-tracking'
```

- `TrackingStep`: below

```ts
interface TrackingStep {
  title: string
  /** ISO date the step was reached, shown as month and day in the item's locale at the end of the step's title row. */
  date?: string
  /** One line under the step, e.g. where the parcel is. */
  note?: string
}
```

## Styling

| Part | Recipe |
| --- | --- |
| `parcel` | [`order-tracking-parcel`](https://ui.frontic.com/DESIGN.md#order-tracking-parcel) |
| `eta` | [`order-tracking-eta`](https://ui.frontic.com/DESIGN.md#order-tracking-eta) |
| `meta` | [`meta`](https://ui.frontic.com/DESIGN.md#meta) |
| `line-title` | [`line-title`](https://ui.frontic.com/DESIGN.md#line-title) |

## Source

`components/sections/order-tracking/OrderTracking.vue`

```vue
<script lang="ts">
/** Shows when a parcel arrives and its delivery steps as a timeline, with the tracking number and link. */
export default {}
</script>

<script setup lang="ts">
import { CheckIcon, TruckIcon, CopyIcon, ExternalLinkIcon } from '@lucide/vue'

import type { HTMLAttributes } from 'vue'
import type { TrackingStep } from './types'
import { ref, toRef } from 'vue'
import { Badge } from '@library/components/ui/badge'
import { Button } from '@library/components/ui/button'
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@library/components/ui/card'
import { Stepper, StepperDescription, StepperIndicator, StepperItem, StepperSeparator, StepperTitle, StepperTrigger } from '@library/components/ui/stepper'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@library/components/ui/tooltip'
import { formatDate } from '@library/lib/format'
import { useLocale } from 'reka-ui'

const props = withDefaults(
  defineProps<{
    /** The card's heading, e.g. the order's reference. */
    title: string
    /** Shown under the heading. */
    description?: string
    /** Shown on the badge beside the heading, e.g. `In transit`. */
    status?: string
    /** The parcel's stages from order to delivery, one stepper item each. */
    steps: TrackingStep[]
    /** The step the parcel is at, 1-based; earlier steps show as done. */
    current: number
    /** The expected delivery, the card's headline, e.g. `Arrives Thursday`. */
    eta?: string
    /** Shown beside the expected delivery, e.g. the time window. */
    etaDetail?: string
    /** The shipping company, shown before the tracking number's label. */
    carrier?: string
    /** The parcel's tracking number, with a button that copies it; the parcel row hides when left out. */
    trackingNumber?: string
    /** The carrier's tracking page, opened in a new tab by the track button; no button when left out. */
    trackingUrl?: string
    /** Shown above the tracking number. */
    trackingNumberLabel?: string
    /** Shown on the button that opens the carrier's tracking page. */
    trackLabel?: string
    /** Shown on the button that asks to change the delivery date. */
    changeDateLabel?: string
    /** Screen-reader name and tooltip of the button that copies the tracking number. */
    copyLabel?: string
    /** Shown in the copy button's tooltip once the number is copied. */
    copiedLabel?: string
    /** Bare drops the edge, shadow and inset, for a section placed inside another Card. */
    surface?: 'raised' | 'bare'
    /** BCP 47 locale the step dates are formatted in; the ConfigProvider's when left out. */
    locale?: string
    class?: HTMLAttributes['class']
  }>(),
  {
    surface: 'raised',
    trackingNumberLabel: 'tracking number',
    trackLabel: 'Track parcel',
    changeDateLabel: 'Change delivery date',
    copyLabel: 'Copy tracking number',
    copiedLabel: 'Copied',
  },
)
const locale = useLocale(toRef(() => props.locale))
const emit = defineEmits<{
  /** Fires when the shopper asks to change the delivery date. */
  changeDate: []
  /** Fires with the tracking number after the shopper copies it to the clipboard. */
  copy: [trackingNumber: string]
}>()

const copied = ref(false)

function copy() {
  if (!props.trackingNumber) return
  void navigator.clipboard?.writeText(props.trackingNumber)
  copied.value = true
  emit('copy', props.trackingNumber)
}
</script>

<template>
  <Card :surface="surface" :class="props.class">
    <CardHeader>
      <CardTitle>{{ title }}</CardTitle>
      <CardDescription v-if="description">{{ description }}</CardDescription>
      <CardAction v-if="status">
        <Badge variant="new" size="sm">{{ status }}</Badge>
      </CardAction>
    </CardHeader>
    <CardContent class="flex flex-col gap-6">
      <div v-if="eta" class="flex flex-col">
        <span data-slot="order-tracking-eta" class="fui-order-tracking-eta">{{ eta }}</span>
        <span v-if="etaDetail" data-slot="meta" class="fui-meta">{{ etaDetail }}</span>
      </div>
      <Stepper :model-value="current" :linear="false" orientation="vertical">
        <StepperItem v-for="(step, index) in steps" :key="index" :step="index + 1" :disabled="index + 1 > current">
          <StepperTrigger>
            <StepperIndicator>
              <CheckIcon v-if="index + 1 < current" data-icon />
              <TruckIcon v-else-if="index + 1 === current" data-icon />
              <template v-else>{{ index + 1 }}</template>
            </StepperIndicator>
            <StepperTitle class="flex items-baseline justify-between gap-3">
              {{ step.title }}
              <time v-if="step.date" data-slot="meta" class="fui-meta" :datetime="step.date">{{ formatDate(step.date, { month: 'short', day: 'numeric' }, locale) }}</time>
            </StepperTitle>
            <StepperDescription v-if="step.note">{{ step.note }}</StepperDescription>
          </StepperTrigger>
          <StepperSeparator v-if="index < steps.length - 1" />
        </StepperItem>
      </Stepper>
      <div v-if="trackingNumber" data-slot="order-tracking-parcel" class="fui-order-tracking-parcel flex items-center justify-between">
        <div class="flex min-w-0 flex-col">
          <span data-slot="meta" class="fui-meta">
            <template v-if="carrier">{{ carrier }} · </template>{{ trackingNumberLabel }}
          </span>
          <span data-slot="line-title" class="fui-line-title truncate tabular-nums">{{ trackingNumber }}</span>
        </div>
        <TooltipProvider>
          <Tooltip>
            <TooltipTrigger as-child>
              <Button variant="ghost" color="neutral" size="icon-sm" :aria-label="copyLabel" @click="copy">
                <CheckIcon v-if="copied" data-icon />
                <CopyIcon v-else data-icon />
              </Button>
            </TooltipTrigger>
            <TooltipContent>{{ copied ? copiedLabel : copyLabel }}</TooltipContent>
          </Tooltip>
        </TooltipProvider>
      </div>
    </CardContent>
    <CardFooter class="flex-col gap-2">
      <Button v-if="trackingUrl" as="a" :href="trackingUrl" target="_blank" rel="noopener" class="w-full">
        {{ trackLabel }}
        <ExternalLinkIcon data-icon />
      </Button>
      <Button variant="ghost" color="neutral" class="w-full" @click="emit('changeDate')">{{ changeDateLabel }}</Button>
    </CardFooter>
  </Card>
</template>
```

`components/sections/order-tracking/types.ts`

```ts
export interface TrackingStep {
  title: string
  /** ISO date the step was reached, shown as month and day in the item's locale at the end of the step's title row. */
  date?: string
  /** One line under the step, e.g. where the parcel is. */
  note?: string
}
```
