---
title: Timeline
desc: >-
  The QTimeline Vue component displays a list of events in chronological order.
  It is typically a graphic design showing a long bar labelled with dates
  alongside itself and usually events.
---
The QTimeline component displays a list of events in chronological order. It is typically a graphic design showing a long bar labelled with dates alongside itself and usually events. Timelines can use any time scale, depending on the subject and data.

QTimeline has 3 layouts:

- `dense` (default) is showing headings, titles, subtitles and content on the **timeline-specified side** of the time line (default on right)
- `comfortable` is showing headings, titles and content on the **timeline-specified side** of the time line (default on right) and the subtitles on the other side
- `loose` is showing headings on center, titles and content on the **entry-specified side** of the time line (default on right) and the subtitles on the other side

## QTimeline API

### Props

- `color` (string, optional), default `'primary'`
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `side` (string, optional), default `'right'`
  Side to place the timeline entries in dense and comfortable layout; For loose layout it gets overridden by QTimelineEntry side prop
  Accepts: `'left'`, `'right'`
- `layout` (string, optional), default `'dense'`
  Layout of the timeline. Dense keeps content and labels on one side. Comfortable keeps content on one side and labels on the opposite side. Loose puts content on both sides.
  Accepts: `'dense'`, `'comfortable'`, `'loose'`
- `dark` (boolean, optional), default `null`
  Notify the component that the background is a dark color

### Slots

- `#default`
  Used for content of component

## QTimelineEntry API

### Props

- `heading` (boolean, optional)
  Defines a heading timeline item
- `tag` (string, optional), default `'h3'`
  Tag to use, if of type 'heading' only
  Examples: `'h1'`
- `side` (string, optional), default `'right'`
  Side to place the timeline entry; Works only if QTimeline layout is loose.
  Accepts: `'left'`, `'right'`
- `icon` (string, optional)
  Icon name following Quasar convention; Make sure you have the icon library installed unless you are using 'img:' prefix; If 'none' (String) is used as value then no icon is rendered (but screen real estate will still be used for it)
  Examples: `'map'`, `'ion-add'`, `'img:https://cdn.quasar.dev/logo-v2/svg/logo.svg'`, `'img:path/to/some_image.png'`
- `avatar` (string, optional)
  URL to the avatar image; Icon takes precedence if used, so it replaces avatar
  Examples: `(public folder) src="img/my-bg.png"`, `(assets folder) src="~@/assets/my-img.png"`, `(relative path format) :src="require('./my_img.jpg')"`, `(URL) src="https://picsum.photos/500/300"`
- `color` (string, optional)
  Color name for component from the Quasar Color Palette
  Examples: `'primary'`, `'teal'`, `'teal-10'`
- `title` (string, optional)
  Title of timeline entry; Is overridden if using 'title' slot
  Examples: `'December party'`
- `subtitle` (string, optional)
  Subtitle of timeline entry; Is overridden if using 'subtitle' slot
  Examples: `'All invited'`
- `body` (string, optional)
  Body content of timeline entry; Use this prop or the default slot
  Examples:
    - `'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'`

### Slots

- `#default`
  Timeline entry content (body)
- `#title`
  Optional slot for title; When used, it overrides 'title' prop
- `#subtitle`
  Optional slot for subtitle; When used, it overrides 'subtitle' prop

## Usage

### Basic

```vue
<template>
  <div class="q-px-lg q-py-md">
    <q-timeline color="secondary">
      <q-timeline-entry heading> Timeline heading </q-timeline-entry>

      <q-timeline-entry title="Event Title" subtitle="February 22, 1986">
        <div>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </q-timeline-entry>

      <!-- ... -->
    </q-timeline>
  </div>
</template>
```

### Using props only

Below is the same example, but using QTimelineEntry properties only instead of the default slot:

### Props only

```vue
<template>
  <div class="q-px-lg q-py-md">
    <q-timeline color="secondary">
      <q-timeline-entry heading body="Timeline heading" />

      <q-timeline-entry
        title="Event Title"
        subtitle="February 22, 1986"
        avatar="https://cdn.quasar.dev/img/avatar3.jpg"
        :body="body"
      />

      <!-- ... -->
    </q-timeline>
  </div>
</template>

<script setup>
const body =
  'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
</script>
```

### Using slots only

Below is again the same example, but using only QTimelineEntry slots:

### Slots only

```vue
<template>
  <div class="q-px-lg q-py-md">
    <q-timeline color="secondary">
      <q-timeline-entry heading> Timeline heading </q-timeline-entry>

      <q-timeline-entry>
        <template v-slot:title> Event Title </template>
        <template v-slot:subtitle> February 22, 1986 </template>

        <div>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </q-timeline-entry>

      <!-- ... -->
    </q-timeline>
  </div>
</template>
```

### Dark design

```vue
<template>
  <div class="q-px-lg q-py-md bg-grey-9 text-white">
    <q-timeline dark color="secondary">
      <q-timeline-entry heading>Timeline heading</q-timeline-entry>

      <q-timeline-entry
        title="Event Title"
        subtitle="February 22, 1986"
        avatar="https://cdn.quasar.dev/img/avatar5.jpg"
      >
        <div>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </q-timeline-entry>

      <!-- ... -->
    </q-timeline>
  </div>
</template>
```

### Layouts and side selection

> [!WARNING]
> QTimelineEntry only takes into account its `side` prop if QTimeline has the `loose` layout.

### Layouts and side selection

```vue
<template>
  <div class="q-pa-lg">
    <div class="row q-gutter-md q-mb-lg">
      <q-option-group
        type="radio"
        dense
        v-model="layout"
        :options="[
          { label: 'Dense layout', value: 'dense' },
          { label: 'Comfortable layout', value: 'comfortable' },
          { label: 'Loose layout', value: 'loose' }
        ]"
      />
      <q-option-group
        type="radio"
        dense
        v-model="side"
        :disable="layout === 'loose'"
        :options="[
          { label: 'Content on right', value: 'right' },
          { label: 'Content on left', value: 'left' }
        ]"
      />
    </div>

    <q-timeline :layout="layout" :side="side" color="secondary">
      <q-timeline-entry heading>Timeline heading</q-timeline-entry>

      <q-timeline-entry
        title="Event Title"
        subtitle="February 22, 1986"
        side="left"
      >
        <div>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </q-timeline-entry>

      <!-- ... -->
    </q-timeline>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const layout = ref('dense')
const side = ref('right')
</script>
```

### Responsive

> [!TIP]
> The examples below uses `$q.screen` to detect changes in window size to see all 3 layouts in action.

### Responsive layout

```vue
<template>
  <div class="q-px-lg q-py-md">
    <q-timeline :layout="layout" color="secondary">
      <q-timeline-entry heading>
        Timeline heading
        <br />
        ({{
          $q.screen.lt.sm ? 'Dense' : $q.screen.lt.md ? 'Comfortable' : 'Loose'
        }}
        layout)
      </q-timeline-entry>

      <q-timeline-entry
        title="Event Title"
        subtitle="February 22, 1986"
        side="left"
      >
        <div>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </q-timeline-entry>

      <!-- ... -->
    </q-timeline>
  </div>
</template>

<script setup>
import { useQuasar } from 'quasar'
import { computed } from 'vue'

const $q = useQuasar()
const layout = computed(() =>
  $q.screen.lt.sm ? 'dense' : $q.screen.lt.md ? 'comfortable' : 'loose'
)
</script>
```

## Accessibility *(v2.25+)*

QTimeline renders a native `<ul>` with each entry as a `<li>`, so entries read as a list. Be aware that entry titles render as `h6` elements regardless of where the timeline sits in your document's heading outline (only a `heading` entry lets you pick its level, through the `tag` prop — an `h3` by default), and that avatar images carry no `alt` attribute — supply meaningful structure and text through the slots and props accordingly.
