
Syncfusion Angular Timeline
- 169 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-timeline for development tasks
About
syncfusion-angular-timeline: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-timeline
Syncfusion Angular Timeline by the numbers
- 169 all-time installs (skills.sh)
- +11 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #2,299 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-timelineAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 169 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-timeline for development tasks
Files
Implementing Syncfusion Angular Timeline
The Timeline component displays a sequence of events or milestones in chronological order, supporting both vertical and horizontal layouts with rich customization options.
When to Use This Skill
- Product Roadmaps: Display product evolution and release milestones
- Project Timelines: Show project phases and deliverables
- Activity Feeds: Display recent activities in reverse chronological order
- Career/Education: Show career progression or educational achievements
- Business Events: Visualize company history, important milestones, or processes
- Trip Itineraries: Display scheduled activities and events
- News Timelines: Show chronological news items or announcements
Component Overview
The Timeline component provides:
- Flexible orientation (vertical/horizontal)
- Multiple content alignments (Before, After, Alternate, AlternateReverse)
- Dual-sided content with
oppositeContent - Event handlers for lifecycle and rendering
- Extensive dot and connector customization
- Template support for complete layout control
- Item-level customization (disabled, styling, content)
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and package setup
- Creating a basic Timeline
- Angular 21 standalone architecture setup
- Importing TimelineModule and TimelineAllModule
- First working example with items
Items and Content
📄 Read: references/items-and-content.md
- String content and opposite content configuration
- Template-based content rendering
- CSS classes for item styling
- Disabled state and dot-item property
- Data binding from arrays
- Content property usage
Alignment
📄 Read: references/alignment.md
- Content positioning with align property
- Before alignment (horizontal & vertical behavior)
- After alignment
- Alternate alignment (zigzag pattern)
- AlternateReverse alignment
- Combining alignment with orientation
Orientations and Reverse
📄 Read: references/orientations-and-reverse.md
- Vertical orientation (default, top-to-bottom)
- Horizontal orientation (left-to-right)
- Reverse property for reversed sequence
- Recent-first timelines
- Reverse with different alignments
- Use cases for activity feeds and news
Customization
📄 Read: references/customization.md
- Connector styling (common and individual item connectors)
- Dot appearance: size, color, outline, shadow, variant
- Dot icons with dotCss property (Material Icons, FontAwesome)
- Item spacing and borders
- CSS class-based customization
- Per-item vs global connector styling
- Advanced visual styling examples
Events
📄 Read: references/events.md
- Created event (component initialization)
- BeforeItemRender event (pre-render customization)
- Event handler setup and binding
- Dynamic item modification via events
- Conditional styling during render
- Event arguments and properties
Templates
📄 Read: references/templates.md
- Template property for complete customization
- Template context (item, itemIndex)
- Custom dot indicator design
- Content area layout restructuring
- Custom connectors via templates
- Advanced layout examples
API Reference
📄 Read: references/api-reference.md
- Complete property documentation with examples
- All component properties explained (including advanced: enablePersistence, enableRtl, locale)
- All item properties documented
- Event binding and handling
- Enumeration values (TimelineAlign, TimelineOrientation)
- Working code examples for every property and event
Quick Start Example
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-root',
template: `
<div class="container">
<ejs-timeline orientation="Vertical" align="Before">
<e-items>
<e-item *ngFor="let item of milestones" [content]="item.content" [oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`.container { padding: 20px; }`]
})
export class AppComponent {
public milestones: TimelineItemModel[] = [
{ content: 'Q1 2024', oppositeContent: 'Planning phase' },
{ content: 'Q2 2024', oppositeContent: 'Development' },
{ content: 'Q3 2024', oppositeContent: 'Testing' },
{ content: 'Q4 2024', oppositeContent: 'Launch' }
];
}Common Patterns
Pattern 1: Activity Feed (Reverse Chronological)
<ejs-timeline [reverse]="true" align="Before">
<e-items>
<e-item *ngFor="let activity of recentActivities" [content]="activity.time" [oppositeContent]="activity.description"></e-item>
</e-items>
</ejs-timeline>When to use: Display latest activities first, news feeds, log entries.
Pattern 2: Alternate Timeline (Balanced Layout)
<ejs-timeline align="Alternate" orientation="Horizontal">
<e-items>
<e-item *ngFor="let event of events" [content]="event.title" [oppositeContent]="event.description"></e-item>
</e-items>
</ejs-timeline>When to use: Visual balance, alternating left/right content, company history.
Pattern 3: Custom Styled Timeline
<ejs-timeline align="Before" [cssClass]="'custom-timeline'">
<e-items>
<e-item *ngFor="let item of items" [content]="item.content" [cssClass]="item.cssClass" [disabled]="item.disabled"></e-item>
</e-items>
</ejs-timeline>When to use: Branded styling, visual differentiation of item states, custom themes.
Key Properties
| Property | Type | Default | Purpose |
|---|---|---|---|
| Layout & Display | |||
orientation | Vertical \ | Horizontal | Vertical |
align | Before \ | After \ | Alternate \ |
reverse | boolean | false | Reverse item order (newest first) |
items | TimelineItemModel[] | [] | Timeline items array |
| Item Content | |||
content | string \ | object | - |
oppositeContent | string \ | object | - |
dotCss | string | - | CSS class for dot icons |
cssClass | string | - | CSS class for item styling |
disabled | boolean | false | Disable individual item |
| Customization | |||
template | string \ | object | - |
cssClass (component) | string | - | Component CSS class |
| Advanced | |||
enablePersistence | boolean | false | Persist state to localStorage |
enableRtl | boolean | false | Right-to-left layout support |
locale | string | en-US | Localization settings |
| Events | |||
created | Event | - | Triggered after component renders |
beforeItemRender | Event | - | Triggered before item renders |
See [references/api-reference.md](references/api-reference.md) for complete details, examples, and advanced configurations.
Common Use Cases
1. Product Roadmap: Horizontal timeline with milestones and features 2. Project Status: Alternate timeline showing phases and progress 3. Career Timeline: Vertical timeline with job positions and dates 4. Company History: Reverse timeline showing company evolution 5. Trip Itinerary: Vertical timeline with location and activity information 6. User Activity Log: Reverse timeline with timestamps and actions 7. News Feed: Horizontal reverse timeline with headlines 8. Educational Path: Vertical alternate timeline showing degrees and courses
Content Alignment
Table of Contents
- Overview
- Before Alignment
- After Alignment
- Alternate Alignment
- AlternateReverse Alignment
- Alignment Combinations
Overview
The Timeline component uses the align property to control how content is positioned relative to the timeline connector. This works with both content and oppositeContent properties to create balanced or directional layouts.
Alignment options:
Before- Content positioned consistently on one sideAfter- Content positioned on opposite side compared to BeforeAlternate- Content alternates sides for zigzag patternAlternateReverse- Reverse zigzag pattern
Before Alignment
Behavior by Orientation
The Before alignment positions content based on the timeline orientation:
Vertical Timeline (default):
contentdisplays on the left side of the timelineoppositeContentdisplays on the right side- Timeline connector runs vertically down the middle
Horizontal Timeline:
contentdisplays on the top of the timelineoppositeContentdisplays on the bottom- Timeline connector runs horizontally
Vertical Before Example
import { CommonModule } from '@angular/common';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { Component } from '@angular/core';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
public frameworks: TimelineItemModel[] = [
{ content: 'ReactJs', oppositeContent: 'Owned by Facebook' },
{ content: 'Angular', oppositeContent: 'Owned by Google' },
{ content: 'VueJs', oppositeContent: 'Owned by Evan you' },
{ content: 'Svelte', oppositeContent: 'Owned by Rich Harris' }
];
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of frameworks"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Result: Left-side content (ReactJs, Angular, etc.) with right-side context (Owned by...).
After Alignment
Behavior by Orientation
The After alignment reverses the positioning compared to Before:
Vertical Timeline:
contentdisplays on the right sideoppositeContentdisplays on the left side
Horizontal Timeline:
contentdisplays on the bottomoppositeContentdisplays on the top
Vertical After Example
export class AppComponent {
public frameworks: TimelineItemModel[] = [
{ content: 'ReactJs', oppositeContent: 'Owned by Facebook' },
{ content: 'Angular', oppositeContent: 'Owned by Google' },
{ content: 'VueJs', oppositeContent: 'Owned by Evan you' },
{ content: 'Svelte', oppositeContent: 'Owned by Rich Harris' }
];
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline align="After">
<e-items>
<e-item *ngFor="let item of frameworks"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Result: Right-side content with left-side context (opposite of Before).
Alternate Alignment
Behavior
The Alternate alignment creates a dynamic zigzag pattern where timeline items alternate sides:
Vertical Timeline:
- Item 1:
contenton left,oppositeContenton right - Item 2:
contenton right,oppositeContenton left - Item 3:
contenton left,oppositeContenton right - And so on...
Horizontal Timeline:
- Item 1:
contenton top,oppositeContenton bottom - Item 2:
contenton bottom,oppositeContenton top - Pattern continues alternating
Visual Impact
Creates balanced, visually interesting layouts that utilize space on both sides of the timeline.
Alternate Example
export class AppComponent {
public frameworks: TimelineItemModel[] = [
{ content: 'ReactJs', oppositeContent: 'Owned by Facebook' },
{ content: 'Angular', oppositeContent: 'Owned by Google' },
{ content: 'VueJs', oppositeContent: 'Owned by Evan you' },
{ content: 'Svelte', oppositeContent: 'Owned by Rich Harris' }
];
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline align="Alternate">
<e-items>
<e-item *ngFor="let item of frameworks"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Use Alternate for:
- Company or product timelines
- Before/After comparisons
- Parallel event tracking
- Maximizing visual space usage
- Visual variety and interest
AlternateReverse Alignment
Behavior
The AlternateReverse alignment creates the reverse pattern of Alternate:
Vertical Timeline:
- Item 1:
contenton right,oppositeContenton left (opposite of Alternate) - Item 2:
contenton left,oppositeContenton right - Pattern continues in reverse alternation
Horizontal Timeline:
- Item 1:
contenton bottom,oppositeContenton top - Item 2:
contenton top,oppositeContenton bottom - Pattern continues
AlternateReverse Example
export class AppComponent {
public frameworks: TimelineItemModel[] = [
{ content: 'ReactJs', oppositeContent: 'Owned by Facebook' },
{ content: 'Angular', oppositeContent: 'Owned by Google' },
{ content: 'VueJs', oppositeContent: 'Owned by Evan you' },
{ content: 'Svelte', oppositeContent: 'Owned by Rich Harris' }
];
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline align="AlternateReverse">
<e-items>
<e-item *ngFor="let item of frameworks"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Use AlternateReverse when:
- You want Alternate pattern but started on opposite side
- Matching specific design requirements
- Coordinating with other UI elements positioning
Alignment Combinations
Alignment + Orientation Combinations
All alignments work with both orientations:
| Alignment | Vertical Behavior | Horizontal Behavior |
|---|---|---|
| Before | Content left, opposite right | Content top, opposite bottom |
| After | Content right, opposite left | Content bottom, opposite top |
| Alternate | L-R-L-R zigzag | T-B-T-B alternation |
| AlternateReverse | R-L-R-L zigzag | B-T-B-T alternation |
Example: Horizontal Alternate
export class AppComponent {
public tripItenerary: TimelineItemModel[] = [
{ content: 'Day 1, 4:00 PM', oppositeContent: 'Check-in and campsite visit' },
{ content: 'Day 1, 7:00 PM', oppositeContent: 'Dinner with music' },
{ content: 'Day 2, 5:30 AM', oppositeContent: 'Sunrise between mountains' },
{ content: 'Day 2, 8:00 AM', oppositeContent: 'Breakfast and check-out' }
];
}<ejs-timeline orientation="Horizontal" align="Alternate">
<e-items>
<e-item *ngFor="let item of tripItenerary"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>Choosing the Right Alignment
Use Before when:
- One-sided display of content
- Timeline with labels on one side only
- Simple chronological listing
Use After when:
- Reversing the Before layout
- Different visual emphasis
- Matching specific design needs
Use Alternate when:
- Balancing content on both sides
- Visual variety is desired
- Material available for both sides
- Creating engaging layouts
Use AlternateReverse when:
- You want Alternate pattern with reversed starting side
- Coordinating with design requirements
- Creating specific visual rhythms
API Reference - Timeline Component
Complete documentation of all Timeline component APIs with detailed explanations and working code examples.
Table of Contents
---
Component Properties
align
Type: string | TimelineAlign Default: After Description: Defines the alignment of item content within the Timeline.
Possible values:
Before- Content on left (vertical) or top (horizontal), opposite content on right/bottomAfter- Content on right (vertical) or bottom (horizontal), opposite content on left/topAlternate- Content alternates between sidesAlternateReverse- Content alternates in reverse pattern
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-alignment',
template: `
<div class="container">
<h2>Alignment: {{ currentAlign }}</h2>
<div class="alignment-selector">
<button (click)="currentAlign = 'Before'">Before</button>
<button (click)="currentAlign = 'After'">After</button>
<button (click)="currentAlign = 'Alternate'">Alternate</button>
<button (click)="currentAlign = 'AlternateReverse'">AlternateReverse</button>
</div>
<ejs-timeline [align]="currentAlign" orientation="Vertical">
<e-items>
<e-item *ngFor="let item of items"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.alignment-selector { margin-bottom: 20px; }
button { margin-right: 10px; padding: 8px 16px; cursor: pointer; }
`]
})
export class AlignmentComponent {
currentAlign = 'Before';
items: TimelineItemModel[] = [
{ content: 'Scheduled', oppositeContent: 'Meeting at 10 AM' },
{ content: 'In Progress', oppositeContent: 'Development phase' },
{ content: 'Completed', oppositeContent: 'Deployment done' }
];
}---
orientation
Type: string | TimelineOrientation Default: Vertical Description: Defines the orientation (layout direction) of the Timeline.
Possible values:
Vertical- Items displayed top-to-bottomHorizontal- Items displayed left-to-right
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-orientation',
template: `
<div class="container">
<h2>Timeline Orientation: {{ orientation }}</h2>
<div class="orientation-selector">
<button (click)="orientation = 'Vertical'">Vertical</button>
<button (click)="orientation = 'Horizontal'">Horizontal</button>
</div>
<div [ngClass]="orientation === 'Horizontal' ? 'horizontal-container' : ''">
<ejs-timeline [orientation]="orientation" align="Before">
<e-items>
<e-item *ngFor="let item of projectSteps"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
</div>
`,
styles: [`
.container { padding: 20px; }
.orientation-selector { margin-bottom: 20px; }
button { margin-right: 10px; padding: 8px 16px; cursor: pointer; }
.horizontal-container { width: 100%; overflow-x: auto; }
.horizontal-container > ejs-timeline { min-width: 1000px; }
`]
})
export class OrientationComponent {
orientation: any = 'Vertical';
projectSteps: TimelineItemModel[] = [
{ content: 'Design', oppositeContent: 'Week 1-2' },
{ content: 'Development', oppositeContent: 'Week 3-6' },
{ content: 'Testing', oppositeContent: 'Week 7-8' },
{ content: 'Deployment', oppositeContent: 'Week 9' }
];
}---
reverse
Type: boolean Default: false Description: Defines whether to display timeline items in reverse order (latest first).
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-reverse',
template: `
<div class="container">
<h2>Activity Feed</h2>
<div class="reverse-toggle">
<label>
<input type="checkbox" [(ngModel)]="isReverse">
Show Recent First
</label>
</div>
<ejs-timeline [reverse]="isReverse" align="Before" orientation="Vertical">
<e-items>
<e-item *ngFor="let activity of activities"
[content]="activity.content"
[oppositeContent]="activity.time"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.reverse-toggle { margin-bottom: 20px; }
label { cursor: pointer; }
`]
})
export class ReverseComponent {
isReverse = true;
activities: TimelineItemModel[] = [
{ content: 'User Registration', time: 'Jan 1, 2024 - 9:00 AM' },
{ content: 'Email Verification', time: 'Jan 2, 2024 - 2:30 PM' },
{ content: 'Profile Completion', time: 'Jan 3, 2024 - 11:15 AM' },
{ content: 'First Purchase', time: 'Jan 5, 2024 - 3:45 PM' },
{ content: 'Account Upgrade', time: 'Jan 8, 2024 - 10:20 AM' }
];
}---
items
Type: TimelineItemModel[] Default: [] Description: Defines the array of timeline items to display.
Example:
import { Component, OnInit } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-items-binding',
template: `
<div class="container">
<h2>Timeline Items Binding</h2>
<div class="controls">
<button (click)="addItem()">Add Item</button>
<button (click)="removeItem()">Remove Last Item</button>
</div>
<p>Total Items: {{ timelineItems.length }}</p>
<ejs-timeline [items]="timelineItems" align="Before">
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.controls { margin-bottom: 20px; }
button { margin-right: 10px; padding: 8px 16px; cursor: pointer; }
`]
})
export class ItemsBindingComponent implements OnInit {
timelineItems: TimelineItemModel[] = [];
ngOnInit() {
this.initializeItems();
}
initializeItems() {
this.timelineItems = [
{ content: 'Item 1', oppositeContent: 'Description 1' },
{ content: 'Item 2', oppositeContent: 'Description 2' },
{ content: 'Item 3', oppositeContent: 'Description 3' }
];
}
addItem() {
const newItem: TimelineItemModel = {
content: `Item ${this.timelineItems.length + 1}`,
oppositeContent: `Description ${this.timelineItems.length + 1}`
};
this.timelineItems = [...this.timelineItems, newItem];
}
removeItem() {
if (this.timelineItems.length > 0) {
this.timelineItems = this.timelineItems.slice(0, -1);
}
}
}---
cssClass
Type: string Default: '' Description: Defines CSS class(es) to customize the Timeline component appearance.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-css-customization',
template: `
<div class="container">
<h2>Custom Styled Timeline</h2>
<ejs-timeline cssClass="custom-timeline" align="Before">
<e-items>
<e-item *ngFor="let item of items"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
/* Custom Timeline Styling */
.custom-timeline.e-timeline .e-timeline-item.e-connector::after {
border-color: #FF6B6B;
border-width: 3px;
}
.custom-timeline.e-timeline .e-dot {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.custom-timeline.e-timeline .e-content {
background: #F8F9FA;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #667eea;
}
`]
})
export class CssCustomizationComponent {
items: TimelineItemModel[] = [
{ content: 'Planning', oppositeContent: 'Phase 1' },
{ content: 'Design', oppositeContent: 'Phase 2' },
{ content: 'Development', oppositeContent: 'Phase 3' },
{ content: 'Testing', oppositeContent: 'Phase 4' }
];
}---
template
Type: string | object Default: '' Description: Defines custom template for rendering timeline items. Template context contains the item model and current index.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-template-example',
template: `
<div class="container">
<h2>Timeline with Custom Template</h2>
<ejs-timeline [template]="itemTemplate" align="Before" orientation="Vertical">
<e-items>
<e-item *ngFor="let item of events; let i = index"
[content]="item.content"
[oppositeContent]="item.date"></e-item>
</e-items>
</ejs-timeline>
</div>
<ng-template #itemTemplate let-data="">
<div class="custom-item-template">
<div class="item-number">{{ data.itemIndex + 1 }}</div>
<div class="item-content">
<h4>{{ data.item.content }}</h4>
<p>{{ data.item.oppositeContent }}</p>
</div>
</div>
</ng-template>
`,
styles: [`
.container { padding: 20px; }
.custom-item-template {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
}
.item-number {
background: #2196F3;
color: white;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.item-content h4 {
margin: 0 0 8px 0;
color: #333;
}
.item-content p {
margin: 0;
color: #666;
font-size: 12px;
}
`]
})
export class TemplateExampleComponent {
events: TimelineItemModel[] = [
{ content: 'Project Kickoff', date: 'Jan 15, 2024' },
{ content: 'Design Phase Complete', date: 'Feb 20, 2024' },
{ content: 'Development Starts', date: 'Mar 1, 2024' },
{ content: 'Testing Phase', date: 'Apr 15, 2024' }
];
}---
locale
Type: string Default: '' (uses global culture, defaults to 'en-US') Description: Overrides the global culture and localization value for this component instance. Useful for displaying locale-specific content, date formats, and translations within a single Timeline component.
Supported Locales: en-US, ar-AE, de-DE, es-ES, fr-FR, ja-JP, zh-CN, and many others
Use Cases:
- Display timeline with locale-specific date formats
- Use different numeric formats for different regions
- Apply locale-specific styling for different markets
- Support multiple languages in different Timeline instances on the same page
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-locale-example',
template: `
<div class="container">
<h2>Timeline with Locale Support</h2>
<div class="locale-selector">
<label>Select Locale:</label>
<select [(ngModel)]="selectedLocale" (change)="onLocaleChange()">
<option value="en-US">English (United States)</option>
<option value="de-DE">Deutsch (Deutschland)</option>
<option value="es-ES">Español (España)</option>
<option value="fr-FR">Français (France)</option>
<option value="ja-JP">日本語 (日本)</option>
<option value="zh-CN">中文 (中国)</option>
<option value="ar-AE">العربية (الإمارات)</option>
</select>
</div>
<div class="timeline-versions">
<div class="timeline-wrapper">
<h3>Timeline with {{ selectedLocale }} locale</h3>
<ejs-timeline
[locale]="selectedLocale"
align="Before"
[items]="getLocalizedItems()">
</ejs-timeline>
</div>
</div>
</div>
`,
styles: [`
.container {
padding: 20px;
max-width: 1000px;
margin: 0 auto;
}
.locale-selector {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 30px;
padding: 15px;
background: #F5F5F5;
border-radius: 4px;
}
.locale-selector label {
font-weight: 500;
color: #333;
}
.locale-selector select {
padding: 8px 12px;
border: 1px solid #DDD;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
.timeline-versions {
display: flex;
gap: 20px;
}
.timeline-wrapper {
flex: 1;
}
.timeline-wrapper h3 {
color: #2196F3;
margin-bottom: 15px;
}
`]
})
export class LocaleExampleComponent {
selectedLocale = 'en-US';
localeFormats: { [key: string]: { dateFormat: string; monthFormat: string } } = {
'en-US': { dateFormat: 'MM/DD/YYYY', monthFormat: 'January' },
'de-DE': { dateFormat: 'DD.MM.YYYY', monthFormat: 'Januar' },
'es-ES': { dateFormat: 'DD/MM/YYYY', monthFormat: 'Enero' },
'fr-FR': { dateFormat: 'DD/MM/YYYY', monthFormat: 'Janvier' },
'ja-JP': { dateFormat: 'YYYY年MM月DD日', monthFormat: '1月' },
'zh-CN': { dateFormat: 'YYYY年MM月DD日', monthFormat: '1月' },
'ar-AE': { dateFormat: 'DD/MM/YYYY', monthFormat: 'يناير' }
};
timelineByLocale: { [key: string]: TimelineItemModel[] } = {
'en-US': [
{ content: 'Concept', oppositeContent: 'January 2024' },
{ content: 'Planning', oppositeContent: 'February 2024' },
{ content: 'Execution', oppositeContent: 'March 2024' },
{ content: 'Review', oppositeContent: 'April 2024' }
],
'de-DE': [
{ content: 'Konzept', oppositeContent: 'Januar 2024' },
{ content: 'Planung', oppositeContent: 'Februar 2024' },
{ content: 'Ausführung', oppositeContent: 'März 2024' },
{ content: 'Überprüfung', oppositeContent: 'April 2024' }
],
'es-ES': [
{ content: 'Concepto', oppositeContent: 'Enero 2024' },
{ content: 'Planificación', oppositeContent: 'Febrero 2024' },
{ content: 'Ejecución', oppositeContent: 'Marzo 2024' },
{ content: 'Revisión', oppositeContent: 'Abril 2024' }
],
'fr-FR': [
{ content: 'Concept', oppositeContent: 'Janvier 2024' },
{ content: 'Planification', oppositeContent: 'Février 2024' },
{ content: 'Exécution', oppositeContent: 'Mars 2024' },
{ content: 'Examen', oppositeContent: 'Avril 2024' }
],
'ja-JP': [
{ content: 'コンセプト', oppositeContent: '2024年1月' },
{ content: '計画', oppositeContent: '2024年2月' },
{ content: '実行', oppositeContent: '2024年3月' },
{ content: 'レビュー', oppositeContent: '2024年4月' }
],
'zh-CN': [
{ content: '概念', oppositeContent: '2024年1月' },
{ content: '规划', oppositeContent: '2024年2月' },
{ content: '执行', oppositeContent: '2024年3月' },
{ content: '审查', oppositeContent: '2024年4月' }
],
'ar-AE': [
{ content: 'الفكرة', oppositeContent: 'يناير 2024' },
{ content: 'التخطيط', oppositeContent: 'فبراير 2024' },
{ content: 'التنفيذ', oppositeContent: 'مارس 2024' },
{ content: 'المراجعة', oppositeContent: 'أبريل 2024' }
]
};
getLocalizedItems(): TimelineItemModel[] {
return this.timelineByLocale[this.selectedLocale] || this.timelineByLocale['en-US'];
}
onLocaleChange() {
console.log('Locale changed to:', this.selectedLocale);
}
}Practical Use Cases:
Case 1: Multilingual Event Timeline
// Display same events in different languages based on user preference
const eventTimeline: { [locale: string]: TimelineItemModel[] } = {
'en-US': [
{ content: 'Conference Starts', oppositeContent: '9:00 AM' },
{ content: 'Keynote Speech', oppositeContent: '10:00 AM - 11:00 AM' }
],
'fr-FR': [
{ content: 'Conférence Commence', oppositeContent: '9h00' },
{ content: 'Discours Principal', oppositeContent: '10h00 - 11h00' }
]
};Case 2: Regional Project Timeline
// Different timelines for different regional markets
<ejs-timeline locale="de-DE">
<!-- German-localized dates and information -->
</ejs-timeline>Case 3: Educational Institution
// Display academic calendar in student's preferred language
ngOnInit() {
// Get student's preferred locale from user settings
this.studentLocale = this.getUserLocale(); // e.g., 'ja-JP'
}---
enablePersistence
Type: boolean Default: false Description: Enable or disable persisting component's internal state (such as scroll position) between page reloads using the browser's localStorage. When enabled, the component saves its state when destroyed and restores it when recreated.
Use Cases:
- Save user's scroll position in a timeline
- Remember expanded/collapsed states across sessions
- Maintain timeline navigation history
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-persistence',
template: `
<div class="container">
<h2>Timeline with Persistence Enabled</h2>
<div class="persistence-info">
<p><strong>Info:</strong> Scroll position and state are saved to localStorage.</p>
<p>Reload the page to see the timeline restore to the previous scroll position.</p>
<button (click)="clearPersistence()" class="clear-btn">Clear Saved State</button>
</div>
<ejs-timeline
id="persistentTimeline"
[enablePersistence]="true"
align="Before"
[items]="longTimelineItems">
</ejs-timeline>
</div>
`,
styles: [`
.container {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.persistence-info {
background: #E3F2FD;
border: 1px solid #2196F3;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
}
.persistence-info p {
margin: 10px 0;
color: #1976D2;
}
.clear-btn {
background: #FF5252;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.clear-btn:hover {
background: #FF1744;
}
`]
})
export class PersistenceComponent {
longTimelineItems: TimelineItemModel[] = [];
constructor() {
this.generateLongTimeline();
}
generateLongTimeline() {
// Generate 50 items for demonstration
for (let i = 1; i <= 50; i++) {
this.longTimelineItems.push({
content: `Event ${i}`,
oppositeContent: `January ${i % 31 + 1}, 2024`
});
}
}
clearPersistence() {
// Clear the localStorage entry for this timeline
const persistenceKey = 'persistentTimeline';
localStorage.removeItem(persistenceKey);
alert('Saved state cleared! The page will reload.');
location.reload();
}
}How It Works: 1. Component state (scroll position, etc.) is automatically saved to localStorage 2. When the component is created again, it reads the saved state and restores it 3. Storage key is based on the component's ID attribute 4. Persisted data is automatically cleared when the component is destroyed properly
---
enableRtl
Type: boolean Default: false Description: Enable or disable rendering the Timeline component in right-to-left (RTL) direction. Used for languages like Arabic, Hebrew, and Persian that are naturally written right-to-left.
Use Cases:
- Support for Arabic language timelines
- Support for Hebrew language timelines
- Support for Persian (Farsi) language timelines
- Multilingual applications with RTL support
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-rtl-support',
template: `
<div class="container">
<h2>RTL Timeline Support</h2>
<div class="language-selector">
<button
(click)="setLanguage('en')"
[class.active]="currentLanguage === 'en'">
English (LTR)
</button>
<button
(click)="setLanguage('ar')"
[class.active]="currentLanguage === 'ar'">
العربية (RTL)
</button>
</div>
<p class="lang-label">Current Language: {{ languageNames[currentLanguage] }}</p>
<ejs-timeline
[enableRtl]="isRtl"
align="Before"
[items]="getRtlItems()"
[attr.dir]="isRtl ? 'rtl' : 'ltr'">
</ejs-timeline>
</div>
`,
styles: [`
.container {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.language-selector {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
button {
padding: 10px 20px;
border: 2px solid #ccc;
background: white;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
transition: all 0.3s ease;
}
button.active {
background: #2196F3;
color: white;
border-color: #2196F3;
}
.lang-label {
font-weight: bold;
color: #333;
margin-bottom: 15px;
}
/* RTL-specific adjustments */
:host[dir="rtl"] .e-timeline {
text-align: right;
}
`]
})
export class RtlSupportComponent {
currentLanguage: 'en' | 'ar' = 'en';
languageNames: { [key: string]: string } = {
'en': 'English (Left-to-Right)',
'ar': 'العربية (Right-to-Left)'
};
timelineData: { [key: string]: TimelineItemModel[] } = {
'en': [
{ content: 'Project Started', oppositeContent: 'January 2024' },
{ content: 'Design Phase', oppositeContent: 'February 2024' },
{ content: 'Development', oppositeContent: 'March - May 2024' },
{ content: 'Testing', oppositeContent: 'June 2024' },
{ content: 'Launch', oppositeContent: 'July 2024' }
],
'ar': [
{ content: 'بدء المشروع', oppositeContent: 'يناير 2024' },
{ content: 'مرحلة التصميم', oppositeContent: 'فبراير 2024' },
{ content: 'التطوير', oppositeContent: 'مارس - مايو 2024' },
{ content: 'الاختبار', oppositeContent: 'يونيو 2024' },
{ content: 'الإطلاق', oppositeContent: 'يوليو 2024' }
]
};
get isRtl(): boolean {
return this.currentLanguage === 'ar';
}
setLanguage(lang: 'en' | 'ar') {
this.currentLanguage = lang;
}
getRtlItems(): TimelineItemModel[] {
return this.timelineData[this.currentLanguage];
}
}Key Features:
- Automatically mirrors the timeline layout
- Content alignment switches automatically
- Connectors and dots are positioned correctly for RTL
- Works with all Timeline alignments and orientations
---
Item Properties
Item properties are defined in the TimelineItemModel interface and applied to individual timeline items.
content
Type: string | object Description: Defines the main text content or template for the Timeline item. Supports HTML and template syntax.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-item-content',
template: `
<div class="container">
<h2>Item Content Examples</h2>
<h3>String Content</h3>
<ejs-timeline align="Before">
<e-items>
<e-item content="Simple text content"></e-item>
<e-item content="Another item with text"></e-item>
</e-items>
</ejs-timeline>
<h3>HTML Content</h3>
<ejs-timeline align="Before" [items]="htmlItems">
</ejs-timeline>
</div>
`,
styles: [`.container { padding: 20px; }`]
})
export class ItemContentComponent {
htmlItems: TimelineItemModel[] = [
{
content: '<div><strong>Project Started</strong><p>Initial phase</p></div>',
oppositeContent: 'Jan 2024'
},
{
content: '<div><strong>Milestone 1</strong><p>Completed successfully</p></div>',
oppositeContent: 'Feb 2024'
}
];
}---
oppositeContent
Type: string | object Description: Defines additional text content or template displayed on the opposite side of the item.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-opposite-content',
template: `
<div class="container">
<h2>Timeline with Opposite Content</h2>
<ejs-timeline align="Alternate" orientation="Vertical">
<e-items>
<e-item *ngFor="let item of timelineData"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`.container { padding: 20px; }`]
})
export class OppositeContentComponent {
timelineData: TimelineItemModel[] = [
{ content: 'Requirement Analysis', oppositeContent: 'Week 1-2' },
{ content: 'System Design', oppositeContent: 'Week 3-4' },
{ content: 'Implementation', oppositeContent: 'Week 5-8' },
{ content: 'Quality Assurance', oppositeContent: 'Week 9-10' }
];
}---
cssClass
Type: string Description: Defines CSS class(es) to customize individual item appearance.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-item-css-class',
template: `
<div class="container">
<h2>Status Timeline with CSS Classes</h2>
<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of statusItems"
[content]="item.content"
[oppositeContent]="item.status"
[cssClass]="'status-' + item.status"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.status-completed .e-content {
background: #D4EDDA;
border-left: 4px solid #28A745;
}
.status-progress .e-content {
background: #FFF3CD;
border-left: 4px solid #FFC107;
}
.status-pending .e-content {
background: #D1ECF1;
border-left: 4px solid #17A2B8;
}
`]
})
export class ItemCssClassComponent {
statusItems: any[] = [
{ content: 'Order Placed', status: 'completed' },
{ content: 'Processing', status: 'progress' },
{ content: 'Shipped', status: 'pending' }
];
}---
disabled
Type: boolean Default: false Description: Defines whether the timeline item is disabled (visually grayed out and not interactive).
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-disabled-items',
template: `
<div class="container">
<h2>Timeline with Disabled Items</h2>
<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of workflowItems"
[content]="item.content"
[oppositeContent]="item.oppositeContent"
[disabled]="item.disabled"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.e-timeline .e-timeline-item.e-disabled .e-content {
opacity: 0.5;
color: #999;
}
`]
})
export class DisabledItemsComponent {
workflowItems: any[] = [
{ content: 'Step 1: Setup', oppositeContent: 'Completed', disabled: false },
{ content: 'Step 2: Configuration', oppositeContent: 'Completed', disabled: false },
{ content: 'Step 3: Testing', oppositeContent: 'In Progress', disabled: false },
{ content: 'Step 4: Deployment', oppositeContent: 'Scheduled', disabled: true },
{ content: 'Step 5: Monitoring', oppositeContent: 'Future', disabled: true }
];
}---
dotCss
Type: string Description: Defines CSS class(es) to include custom icons or images in the Timeline item dot. Useful for displaying FontAwesome, Material Icons, or custom SVG icons.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-dot-css-icons',
template: `
<div class="container">
<h2>Timeline with Icon Dots</h2>
<p>Using Material Icons in Timeline dots</p>
<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of processSteps"
[content]="item.content"
[oppositeContent]="item.phase"
[dotCss]="item.dotCss"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
/* Material Icon styling in dot */
.e-timeline .e-dot::before {
font-family: 'Material Icons';
font-size: 18px;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
/* Icon-specific styles */
.icon-design::before {
content: '\\e3fd'; /* design_services icon */
}
.icon-develop::before {
content: '\\e86e'; /* code icon */
}
.icon-test::before {
content: '\\e8f0'; /* bug_report icon */
}
.icon-deploy::before {
content: '\\e2c4'; /* cloud_upload icon */
}
/* Color coding */
.icon-design .e-dot {
background-color: #6366F1;
}
.icon-develop .e-dot {
background-color: #8B5CF6;
}
.icon-test .e-dot {
background-color: #EC4899;
}
.icon-deploy .e-dot {
background-color: #10B981;
}
`]
})
export class DotCssIconsComponent {
processSteps: any[] = [
{
content: 'UI/UX Design',
phase: 'Week 1-2',
dotCss: 'icon-design'
},
{
content: 'Backend Development',
phase: 'Week 3-5',
dotCss: 'icon-develop'
},
{
content: 'QA Testing',
phase: 'Week 6-7',
dotCss: 'icon-test'
},
{
content: 'Production Deploy',
phase: 'Week 8',
dotCss: 'icon-deploy'
}
];
}---
Events
created
Type: EmitType<Event> Description: Event callback that is raised after the Timeline component has finished rendering and is ready for interaction.
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-created-event',
template: `
<div class="container">
<h2>Timeline Created Event Example</h2>
<p [ngClass]="{ 'success': isTimelineReady }">
Timeline Status: {{ isTimelineReady ? 'Ready ✓' : 'Loading...' }}
</p>
<ejs-timeline (created)="onTimelineCreated()" align="Before">
<e-items>
<e-item *ngFor="let item of items"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.success { color: #28A745; font-weight: bold; }
`]
})
export class CreatedEventComponent {
isTimelineReady = false;
items: TimelineItemModel[] = [
{ content: 'Event 1', oppositeContent: 'Description 1' },
{ content: 'Event 2', oppositeContent: 'Description 2' },
{ content: 'Event 3', oppositeContent: 'Description 3' }
];
onTimelineCreated() {
console.log('Timeline component has been created and rendered!');
this.isTimelineReady = true;
// Perform initialization logic here
this.initializeTimeline();
}
initializeTimeline() {
console.log('Timeline ready with', this.items.length, 'items');
// Load additional data, trigger animations, etc.
}
}---
beforeItemRender
Type: EmitType<TimelineRenderingEventArgs> Description: Event that triggers before rendering each individual Timeline item. Allows you to modify item properties, apply conditional styling, or skip items.
Event Arguments:
data(TimelineItemModel) - The current item being renderedelement(HTMLElement) - The DOM element being renderedindex(number) - Zero-based index of the current itemname(string) - Name of the event ('beforeItemRender')
Example:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule, TimelineRenderingEventArgs } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-before-item-render',
template: `
<div class="container">
<h2>BeforeItemRender Event Example</h2>
<p>Items with status "Current" are highlighted</p>
<ejs-timeline (beforeItemRender)="onBeforeItemRender($event)" align="Before">
<e-items>
<e-item *ngFor="let item of statusItems"
[content]="item.content"
[oppositeContent]="item.status"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.current-item .e-content {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-weight: bold;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.current-item .e-dot {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 0 15px rgba(102, 126, 234, 0.6);
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 15px rgba(102, 126, 234, 0.6); }
50% { box-shadow: 0 0 25px rgba(102, 126, 234, 0.8); }
}
.past-item, .future-item { opacity: 0.6; }
.future-item .e-dot {
background-color: #BDBDBD;
}
`]
})
export class BeforeItemRenderComponent {
statusItems: any[] = [
{ content: 'Order Placed', status: 'Completed' },
{ content: 'Processing', status: 'Current' },
{ content: 'Shipped', status: 'Pending' },
{ content: 'Delivery', status: 'Pending' }
];
onBeforeItemRender(args: TimelineRenderingEventArgs) {
const index = args.index;
const element = args.element;
const totalItems = this.statusItems.length;
// Highlight current item (index 1 in this example)
if (index === 1) {
element.classList.add('current-item');
console.log('Current item index:', index);
} else if (index < 1) {
element.classList.add('past-item');
} else {
element.classList.add('future-item');
}
}
}Advanced BeforeItemRender Example - Skip Items:
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule, TimelineRenderingEventArgs } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-skip-items',
template: `
<div class="container">
<h2>Skip Items Example</h2>
<label>
<input type="checkbox" [(ngModel)]="showInternalOnly">
Show Internal Events Only
</label>
<ejs-timeline (beforeItemRender)="onBeforeItemRender($event)" align="Before">
<e-items>
<e-item *ngFor="let event of events"
[content]="event.content"
[oppositeContent]="event.visibility"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`.container { padding: 20px; } label { cursor: pointer; }`]
})
export class SkipItemsComponent {
showInternalOnly = false;
events: any[] = [
{ content: 'Public Launch', visibility: 'Public' },
{ content: 'Team Meeting', visibility: 'Internal' },
{ content: 'Board Review', visibility: 'Internal' },
{ content: 'Press Conference', visibility: 'Public' }
];
onBeforeItemRender(args: TimelineRenderingEventArgs) {
// Hide items based on condition - set cancel = true to skip rendering
if (this.showInternalOnly && args.data.oppositeContent === 'Public') {
// Skip rendering this item
// Note: In some versions, you may need to use args.cancel = true
args.element.style.display = 'none';
} else if (!this.showInternalOnly && args.data.oppositeContent === 'Internal') {
args.element.style.display = 'none';
}
}
}---
Enumerations
TimelineAlign
Specifies the alignment of item content within the Timeline.
enum TimelineAlign {
After = 'After',
Before = 'Before',
Alternate = 'Alternate',
AlternateReverse = 'AlternateReverse'
}---
TimelineOrientation
Defines the orientation type of the Timeline.
enum TimelineOrientation {
Horizontal = 'Horizontal',
Vertical = 'Vertical'
}---
Customization
Table of Contents
- Connector Styling
- Dot Appearance
- Dot Size Customization
- Dot Color Customization
- Dot Outline and Effects
- Dot Variant Styles
- Dot Icons with dotCss Property
- Item-Level Styling
- Advanced Examples
Connector Styling
Common Connector Styling
Apply styles to all Timeline item connectors for consistent presentation across the entire Timeline.
Global Connector CSS
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [TimelineModule, TimelineAllModule, CommonModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
public dailyRoutine: TimelineItemModel[] = [
{ content: 'Eat' },
{ content: 'Code' },
{ content: 'Repeat' }
];
}<ejs-timeline align="Before" cssClass="custom-timeline">
<e-items>
<e-item *ngFor="let item of dailyRoutine" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>/* app.component.css */
.custom-timeline .e-timeline-item.e-connector::after {
border-color: #2196F3;
border-width: 3px;
}Key CSS selectors:
.e-connector::after- Connector line between dots.e-timeline-item- Timeline item container.e-content- Main content area.e-opposite-content- Opposite side content
Individual Connector Styling
Style connectors for specific items using CSS classes:
export class AppComponent {
public dailyRoutine: TimelineItemModel[] = [
{ content: 'Eat', cssClass: 'activity-food' },
{ content: 'Code', cssClass: 'activity-work' },
{ content: 'Repeat', cssClass: 'activity-repeat' }
];
}<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of dailyRoutine"
[content]="item.content"
[cssClass]="item.cssClass"></e-item>
</e-items>
</ejs-timeline>.e-timeline-item.activity-food.e-connector::after {
border-color: #FF9800;
border-width: 2px;
}
.e-timeline-item.activity-work.e-connector::after {
border-color: #2196F3;
border-width: 3px;
}
.e-timeline-item.activity-repeat.e-connector::after {
border-color: #4CAF50;
border-width: 2px;
border-style: dashed;
}Use this when: Different phases have different visual connectors, status indicators, or priority levels.
Dot Appearance
Dot Size Customization
Customize the size of timeline dots using CSS:
export class AppComponent {
public milestones: TimelineItemModel[] = [
{ content: 'Phase 1', cssClass: 'dot-sm' },
{ content: 'Phase 2', cssClass: 'dot-md' },
{ content: 'Phase 3', cssClass: 'dot-lg' }
];
}<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of milestones"
[content]="item.content"
[cssClass]="item.cssClass"></e-item>
</e-items>
</ejs-timeline>.dot-sm .e-dot {
min-width: 12px;
min-height: 12px;
}
.dot-md .e-dot {
min-width: 20px;
min-height: 20px;
}
.dot-lg .e-dot {
min-width: 28px;
min-height: 28px;
}Dot Color Customization
export class AppComponent {
public orderStatus: TimelineItemModel[] = [
{ content: 'Processing', cssClass: 'status-pending' },
{ content: 'Shipped', cssClass: 'status-progress' },
{ content: 'Delivered', cssClass: 'status-completed' }
];
}<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of orderStatus"
[content]="item.content"
[cssClass]="item.cssClass"></e-item>
</e-items>
</ejs-timeline>.status-pending .e-dot {
background-color: #FF9800;
border: 2px solid #F57C00;
}
.status-progress .e-dot {
background-color: #2196F3;
border: 2px solid #1976D2;
}
.status-completed .e-dot {
background-color: #4CAF50;
border: 2px solid #388E3C;
}Dot Outline Customization
Add borders and outlines to dots:
<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of items"
[content]="item.content"
[cssClass]="'outlined-dot'"></e-item>
</e-items>
</ejs-timeline>.outlined-dot .e-dot {
background-color: white;
border: 3px solid #2196F3;
outline: 2px solid #E3F2FD;
}Dot Shadow Effects
Create depth with shadows:
.dot-shadow .e-dot {
background-color: #2196F3;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.dot-shadow-large .e-dot {
background-color: #4CAF50;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}Dot Variant Styles
Create different dot styles for various scenarios:
export class AppComponent {
public workflow: TimelineItemModel[] = [
{ content: 'Pending', cssClass: 'dot-variant-pending' },
{ content: 'In Progress', cssClass: 'dot-variant-progress' },
{ content: 'Completed', cssClass: 'dot-variant-completed' },
{ content: 'Archived', cssClass: 'dot-variant-archived' }
];
}<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of workflow"
[content]="item.content"
[cssClass]="item.cssClass"></e-item>
</e-items>
</ejs-timeline>/* Default circle */
.dot-variant-pending .e-dot {
background-color: #ECEFF1;
border: 2px solid #90A4AE;
}
/* Progress - filled circle */
.dot-variant-progress .e-dot {
background-color: #FFC107;
border: 2px solid #FBC02D;
animation: pulse 2s infinite;
}
/* Completed - checkmark style */
.dot-variant-completed .e-dot {
background-color: #4CAF50;
border: 2px solid #2E7D32;
}
.dot-variant-completed .e-dot::before {
content: '✓';
color: white;
font-weight: bold;
}
/* Archived - muted */
.dot-variant-archived .e-dot {
background-color: #BDBDBD;
border: 2px solid #757575;
opacity: 0.6;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
50% { box-shadow: 0 0 0 8px rgba(255, 193, 7, 0); }
}Dot Icons with dotCss Property
Use the dotCss property to add custom icons or images to timeline dots. This is ideal for displaying status indicators, FontAwesome icons, or Material Design icons.
dotCss Property:
- Type:
string - Description: CSS class(es) applied to the dot element, allowing you to display icons or images using CSS content property or backgroundImage
- Use Case: Add FontAwesome, Material Icons, or custom SVG icons to timeline dots
Example 1: Material Design Icons
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [TimelineModule, TimelineAllModule, CommonModule],
standalone: true,
selector: 'app-dot-icons',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public softwareDevelopment: TimelineItemModel[] = [
{
content: 'Requirements Gathering',
oppositeContent: 'Week 1-2',
dotCss: 'icon-requirements'
},
{
content: 'System Design',
oppositeContent: 'Week 3-4',
dotCss: 'icon-design'
},
{
content: 'Development',
oppositeContent: 'Week 5-7',
dotCss: 'icon-code'
},
{
content: 'Testing',
oppositeContent: 'Week 8-9',
dotCss: 'icon-test'
},
{
content: 'Deployment',
oppositeContent: 'Week 10',
dotCss: 'icon-deploy'
}
];
}<ejs-timeline align="Before" cssClass="icon-timeline">
<e-items>
<e-item *ngFor="let item of softwareDevelopment"
[content]="item.content"
[oppositeContent]="item.oppositeContent"
[dotCss]="item.dotCss"></e-item>
</e-items>
</ejs-timeline>/* Styles for icon timeline */
.icon-timeline .e-dot::before {
font-family: 'Material Icons';
font-size: 20px;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
/* Individual icon styles with Material Icons Unicode */
.icon-requirements .e-dot {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.icon-requirements .e-dot::before {
content: '\\e8e8'; /* assignment icon */
}
.icon-design .e-dot {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.icon-design .e-dot::before {
content: '\\e3fd'; /* design_services icon */
}
.icon-code .e-dot {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.icon-code .e-dot::before {
content: '\\e86e'; /* code icon */
}
.icon-test .e-dot {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.icon-test .e-dot::before {
content: '\\e8f0'; /* bug_report icon */
}
.icon-deploy .e-dot {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}
.icon-deploy .e-dot::before {
content: '\\e2c4'; /* cloud_upload icon */
}
/* Hover effect */
.icon-timeline .e-timeline-item:hover .e-dot {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}Example 2: FontAwesome Icons
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [TimelineModule, TimelineAllModule, CommonModule],
standalone: true,
selector: 'app-font-awesome-icons',
template: `
<div class="container">
<h2>Timeline with FontAwesome Icons</h2>
<ejs-timeline align="Before" cssClass="fa-timeline">
<e-items>
<e-item *ngFor="let event of events"
[content]="event.content"
[oppositeContent]="event.date"
[dotCss]="event.dotCss"></e-item>
</e-items>
</ejs-timeline>
</div>
`,
styles: [`
.container { padding: 20px; }
.fa-timeline .e-dot {
background: white;
border: 3px solid #2196F3;
font-size: 0;
}
.fa-timeline .e-dot::before {
font-family: 'FontAwesome';
font-size: 20px;
color: #2196F3;
}
.icon-user .e-dot::before { content: '\\f007'; }
.icon-check .e-dot::before { content: '\\f05d'; }
.icon-flag .e-dot::before { content: '\\f024'; }
.icon-star .e-dot::before { content: '\\f005'; }
`]
})
export class FontAwesomeIconsComponent {
events: any[] = [
{ content: 'User Registration', date: 'Jan 1', dotCss: 'icon-user' },
{ content: 'Email Verified', date: 'Jan 2', dotCss: 'icon-check' },
{ content: 'Profile Marked', date: 'Jan 3', dotCss: 'icon-flag' },
{ content: 'Premium Status', date: 'Jan 5', dotCss: 'icon-star' }
];
}Example 3: Custom Image Backgrounds
/* Using background images in dots */
.icon-custom .e-dot {
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.icon-success .e-dot {
background-image: url('assets/icons/success.svg');
background-color: #E8F5E9;
}
.icon-warning .e-dot {
background-image: url('assets/icons/warning.svg');
background-color: #FFF3E0;
}
.icon-error .e-dot {
background-image: url('assets/icons/error.svg');
background-color: #FFEBEE;
}Best Practices for dotCss:
1. Icon Font Setup: Ensure MaterialIcons, FontAwesome, or other icon fonts are properly imported 2. Color Coding: Use different colors for different icon types to improve visual distinction 3. Size Consistency: Keep dot sizes consistent when using icons 4. Accessibility: Provide meaningful content descriptions with oppositeContent 5. Performance: Use CSS sprites or web fonts for better performance over individual images
Item-Level Styling
Content Container Styling
<ejs-timeline [cssClass]="'styled-timeline'" align="Before">
<e-items>
<e-item *ngFor="let item of items"
[content]="item.content"
[cssClass]="item.cssClass"></e-item>
</e-items>
</ejs-timeline>.styled-timeline .e-content {
padding: 15px;
background-color: #F5F5F5;
border-radius: 4px;
border-left: 4px solid #2196F3;
}
.styled-timeline .e-timeline-item:hover .e-content {
background-color: #E3F2FD;
box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);
transition: all 0.3s ease;
}Spacing and Layout
.custom-timeline .e-timeline {
padding: 20px;
}
.custom-timeline .e-timeline-item {
margin-bottom: 10px;
}
/* Horizontal spacing */
.custom-timeline .e-timeline-item:not(:last-child).e-connector::after {
height: 60px;
}Advanced Examples
Multi-Status Timeline
export class AppComponent {
public projectStatus: TimelineItemModel[] = [
{ content: 'Design Approved', oppositeContent: 'March 15', cssClass: 'status-completed' },
{ content: 'Development Sprint 1', oppositeContent: 'In Progress', cssClass: 'status-progress' },
{ content: 'Code Review', oppositeContent: 'Pending', cssClass: 'status-pending' },
{ content: 'QA Testing', oppositeContent: 'Scheduled', cssClass: 'status-scheduled' },
{ content: 'Deployment', oppositeContent: 'Future', cssClass: 'status-future', disabled: true }
];
}<ejs-timeline align="Alternate" [cssClass]="'multi-status'">
<e-items>
<e-item *ngFor="let item of projectStatus"
[content]="item.content"
[oppositeContent]="item.oppositeContent"
[cssClass]="item.cssClass"
[disabled]="item.disabled"></e-item>
</e-items>
</ejs-timeline>.multi-status .status-completed .e-dot {
background-color: #4CAF50;
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3);
}
.multi-status .status-progress .e-dot {
background-color: #2196F3;
animation: pulse-blue 2s infinite;
}
.multi-status .status-pending .e-dot {
background-color: #FF9800;
}
.multi-status .status-scheduled .e-dot {
background-color: #9C27B0;
}
.multi-status .status-future .e-dot {
background-color: #BDBDBD;
opacity: 0.5;
}
.multi-status .e-timeline-item.status-completed.e-connector::after {
border-color: #4CAF50;
}
.multi-status .e-timeline-item.status-progress.e-connector::after {
border-color: #2196F3;
}
@keyframes pulse-blue {
0%, 100% { box-shadow: 0 0 0 0 rgba(33, 150, 243, 0.7); }
50% { box-shadow: 0 0 0 10px rgba(33, 150, 243, 0); }
}Responsive Customization
/* Mobile: Stack vertically, reduce dot size */
@media (max-width: 768px) {
.responsive-timeline.e-timeline {
--dot-size: 16px;
}
.responsive-timeline .e-content {
padding: 10px;
font-size: 14px;
}
}
/* Tablet: Medium sizing */
@media (min-width: 768px) and (max-width: 1024px) {
.responsive-timeline.e-timeline {
--dot-size: 18px;
}
}Events
Table of Contents
Overview
The Timeline component triggers events at different lifecycle stages, allowing you to execute custom logic and modify behavior through event handlers.
Available events:
created- Fires when Timeline rendering is completebeforeItemRender- Fires before each item renders, allowing customization
Both events can be bound using Angular's event binding syntax (eventName)="handler($event)".
Created Event
Overview
The created event fires once when the Timeline component has finished rendering and is ready for interaction. This is useful for initialization logic, setup operations, or triggering other components.
Basic Created Event
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [TimelineModule, TimelineAllModule, CommonModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
public productLifecycle: TimelineItemModel[] = [
{ content: 'Planning'},
{ content: 'Developing'},
{ content: 'Testing' },
{ content: 'Launch' },
];
handleTimelineCreated = () => {
console.log('Timeline component is ready!');
// Your initialization code here
}
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline (created)="handleTimelineCreated()">
<e-items>
<e-item *ngFor="let item of productLifecycle" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>
</div>Use Cases for Created Event
- Initialization: Set up dependent components or services
- Analytics: Track when Timeline becomes visible/interactive
- Dynamic loading: Load additional data after Timeline renders
- Focus management: Set focus to specific elements
- Animations: Trigger entrance animations after render
Created with Initialization Logic
export class AppComponent {
public timeline: TimelineItemModel[] = [];
public isLoading = true;
constructor(private dataService: DataService) {}
handleTimelineCreated = () => {
this.isLoading = false;
console.log('Timeline ready with', this.timeline.length, 'items');
this.logAnalyticsEventTimelineLoaded();
}
ngOnInit() {
this.dataService.getTimeline().subscribe(data => {
this.timeline = data;
});
}
private logAnalyticsEventTimelineLoaded() {
// Send event to analytics service
}
}BeforeItemRender Event
Overview
The beforeItemRender event fires before each individual Timeline item renders, allowing you to:
- Modify item properties dynamically
- Apply conditional styling
- Add custom attributes
- Skip rendering specific items
- Create dynamic content based on item index
Event Arguments
The event receives TimelineRenderingEventArgs with:
data- The Timeline item being renderedindex- Index of the itemelement- DOM element being renderedcancel- Boolean to prevent rendering (if true, item is skipped)
Basic BeforeItemRender Example
import { TimelineRenderingEventArgs } from "@syncfusion/ej2-angular-layouts";
export class AppComponent {
public productLifecycle: TimelineItemModel[] = [
{ content: 'Planning'},
{ content: 'Developing'},
{ content: 'Testing' },
{ content: 'Launch' },
];
handleBeforeItemRender = (args: TimelineRenderingEventArgs) => {
console.log('Rendering item:', args.data.content, 'at index:', args.index);
}
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline (beforeItemRender)="handleBeforeItemRender($event)">
<e-items>
<e-item *ngFor="let item of productLifecycle" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>
</div>Conditional Styling Based on Index
export class AppComponent {
public roadmap: TimelineItemModel[] = [
{ content: 'Q1 Planning' },
{ content: 'Q2 Development' },
{ content: 'Q3 Testing' },
{ content: 'Q4 Launch' }
];
handleBeforeItemRender = (args: TimelineRenderingEventArgs) => {
const index = args.index;
// Highlight current item
if (index === 1) {
args.element.classList.add('current-phase');
}
// Mark future items
if (index > 1) {
args.element.classList.add('future-phase');
}
}
}<ejs-timeline (beforeItemRender)="handleBeforeItemRender($event)" align="Alternate">
<e-items>
<e-item *ngFor="let item of roadmap" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>.current-phase {
background-color: #FFC107 !important;
}
.current-phase .e-dot {
min-width: 24px;
min-height: 24px;
background-color: #FBC02D;
box-shadow: 0 0 10px rgba(255, 193, 7, 0.5);
}
.future-phase {
opacity: 0.6;
}
.future-phase .e-dot {
background-color: #BDBDBD;
}Modifying Item Content Dynamically
export class AppComponent {
public workflow: TimelineItemModel[] = [
{ content: 'Step 1', status: 'completed' },
{ content: 'Step 2', status: 'progress' },
{ content: 'Step 3', status: 'pending' }
];
handleBeforeItemRender = (args: TimelineRenderingEventArgs) => {
const item = args.data;
// Add status badge to content
if (item.status === 'completed') {
item.content += ' ✓';
} else if (item.status === 'progress') {
item.content += ' ⟳';
}
// Apply status-based CSS
args.element.classList.add(`status-${item.status}`);
}
}.status-completed {
border-left: 4px solid #4CAF50;
}
.status-progress {
border-left: 4px solid #2196F3;
}
.status-pending {
border-left: 4px solid #FF9800;
}Skipping Items Based on Conditions
export class AppComponent {
public allEvents: TimelineItemModel[] = [
{ content: 'Public Announcement', visibility: 'public' },
{ content: 'Internal Review', visibility: 'internal' },
{ content: 'Team Meeting', visibility: 'internal' },
{ content: 'Product Launch', visibility: 'public' }
];
isPublicView = true;
handleBeforeItemRender = (args: TimelineRenderingEventArgs) => {
// Hide internal items in public view
if (this.isPublicView && args.data.visibility === 'internal') {
args.cancel = true;
}
}
}<div>
<label>
<input type="checkbox" [checked]="isPublicView" (change)="isPublicView = !isPublicView">
Public View Only
</label>
<ejs-timeline (beforeItemRender)="handleBeforeItemRender($event)">
<e-items>
<e-item *ngFor="let item of allEvents" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>
</div>Common Event Patterns
Pattern 1: Highlight Current Item
export class AppComponent {
public steps: TimelineItemModel[] = [
{ content: 'Step 1' },
{ content: 'Step 2' },
{ content: 'Step 3' }
];
currentStep = 1;
onBeforeItemRender = (args: TimelineRenderingEventArgs) => {
if (args.index === this.currentStep) {
args.element.classList.add('highlight');
}
}
}Use for: Step-by-step wizards, process indicators.
Pattern 2: Alternate Content Color
export class AppComponent {
public events: TimelineItemModel[] = [];
onBeforeItemRender = (args: TimelineRenderingEventArgs) => {
const isEven = args.index % 2 === 0;
args.element.classList.add(isEven ? 'even-item' : 'odd-item');
}
}.even-item { background-color: #F5F5F5; }
.odd-item { background-color: #FFFFFF; }Use for: Enhanced visual separation.
Pattern 3: Dynamic Loading on Timeline Creation
export class AppComponent {
public timeline: TimelineItemModel[] = [];
onTimelineCreated = () => {
this.loadTimelineData();
}
private loadTimelineData() {
this.dataService.fetchTimeline().subscribe(data => {
this.timeline = data;
});
}
}Use for: Lazy loading, data-dependent initialization.
Pattern 4: Index-Based Styling
export class AppComponent {
public quarters: TimelineItemModel[] = [
{ content: 'Q1' },
{ content: 'Q2' },
{ content: 'Q3' },
{ content: 'Q4' }
];
onBeforeItemRender = (args: TimelineRenderingEventArgs) => {
const colorMap = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A'];
args.element.style.borderLeftColor = colorMap[args.index];
}
}Use for: Rainbow/gradient timelines, category-based colors.
Getting Started with Angular Timeline
Table of Contents
- Installation
- Angular Environment Setup
- Creating Your First Timeline
- Adding Items
- CSS Imports
- Complete Working Example
Installation
Step 1: Install Required Package
Install the @syncfusion/ej2-angular-layouts package which contains the Timeline component:
npm install @syncfusion/ej2-angular-layoutsStep 2: Verify Dependencies
The Timeline module depends on:
@syncfusion/ej2-angular-layouts
|-- @syncfusion/ej2-base
|-- @syncfusion/ej2-layouts
|-- @syncfusion/ej2-angular-baseAll dependencies are automatically installed with the main package.
Angular Environment Setup
Using Angular 21 (Standalone Architecture)
Angular 21 uses standalone components by default. Here's the recommended setup:
npm install -g @angular/cli@21.0.0
ng new my-timeline-app
cd my-timeline-appNote: If you need traditional NgModule architecture, configure your component with @NgModule() decorator instead of standalone components.Creating Your First Timeline
Step 1: Import TimelineModule
In your component, import the required modules:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TimelineModule, TimelineAllModule } from '@syncfusion/ej2-angular-layouts';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-root',
template: `
<div class="container">
<ejs-timeline>
<e-items>
<e-item></e-item>
</e-items>
</ejs-timeline>
</div>
`
})
export class AppComponent { }Key imports:
TimelineModule- Main Timeline componentTimelineAllModule- Includes all Timeline-related modulesCommonModule- For*ngForand structural directives
Step 2: Add Container Styling
Set container height and spacing:
.container {
height: 330px;
margin-top: 30px;
width: 100%;
}Adding Items
Basic Items with Content
Add items using the <e-item> directive with content:
import { TimelineItemModel } from '@syncfusion/ej2-angular-layouts';
export class AppComponent {
public items: TimelineItemModel[] = [
{ content: 'Event 1' },
{ content: 'Event 2' },
{ content: 'Event 3' },
{ content: 'Event 4' }
];
}<ejs-timeline>
<e-items>
<e-item *ngFor="let item of items" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>Items with Opposite Content
Display content on both sides:
public items: TimelineItemModel[] = [
{ content: 'Q1 2024', oppositeContent: 'Planning' },
{ content: 'Q2 2024', oppositeContent: 'Development' },
{ content: 'Q3 2024', oppositeContent: 'Testing' },
{ content: 'Q4 2024', oppositeContent: 'Launch' }
];<ejs-timeline>
<e-items>
<e-item *ngFor="let item of items"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>CSS Imports
Required Styles
Add Timeline styles to your global styles.css:
@import '@syncfusion/ej2-angular-layouts/styles/timeline/material.css';Available Themes
Choose one theme:
material.css- Material Design themebootstrap.css- Bootstrap themefabric.css- Fabric theme
Place the import at the top of styles.css before other styles.
Component-level Styles
You can also import in component styleUrls:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})/* app.component.css */
@import '@syncfusion/ej2-angular-layouts/styles/timeline/material.css';Complete Working Example
app.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from '@syncfusion/ej2-angular-layouts';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public productReleases: TimelineItemModel[] = [
{ content: 'v1.0.0', oppositeContent: 'Initial release' },
{ content: 'v1.1.0', oppositeContent: 'Bug fixes and improvements' },
{ content: 'v2.0.0', oppositeContent: 'Major feature update' },
{ content: 'v2.1.0', oppositeContent: 'Performance optimization' }
];
}app.component.html
<div class="container">
<h2>Product Release Timeline</h2>
<ejs-timeline orientation="Vertical" align="Before">
<e-items>
<e-item *ngFor="let item of productReleases"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>app.component.css
@import '@syncfusion/ej2-angular-layouts/styles/timeline/material.css';
.container {
max-width: 800px;
margin: 0 auto;
padding: 30px 20px;
font-family: Arial, sans-serif;
}
.container h2 {
text-align: center;
margin-bottom: 30px;
color: #333;
}main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import 'zone.js';
bootstrapApplication(AppComponent)
.catch((err) => console.error(err));Troubleshooting
Issue: Timeline items not displaying
Solution:
- Ensure
TimelineModuleis imported in the component - Verify CSS theme is imported in
styles.css - Check that
e-itemsande-itemdirectives are used correctly
Issue: Styles not applying
Solution:
- Confirm theme CSS import path is correct
- Clear browser cache and rebuild
- Check console for CSS import errors
Issue: Items not binding from data
Solution:
- Verify
*ngForloop syntax is correct - Ensure data array is properly initialized
- Check property bindings with
[content]format
Items and Content Configuration
Table of Contents
- Adding Content
- Opposite Content
- CSS Classes for Items
- Disabled Items
- Dot Item Property
- Dot Icons with dotCss
- Data Binding from Arrays
Adding Content
String Content
Define simple text content for Timeline items:
import { TimelineItemModel } from '@syncfusion/ej2-angular-layouts';
export class AppComponent {
public orderStatus: TimelineItemModel[] = [
{ content: 'Shipped' },
{ content: 'Departed' },
{ content: 'Arrived' },
{ content: 'Out for Delivery' }
];
}<ejs-timeline>
<e-items>
<e-item *ngFor="let item of orderStatus" [content]="item.content"></e-item>
</e-items>
</ejs-timeline>Use this when: Single line status updates, simple event labels, or minimal content.
Template-based Content
Use Angular templates for complex content with HTML and styles:
export class AppComponent {
public milestones: TimelineItemModel[] = [
{
content: 'Phase 1',
data: { date: 'Jan 2024', description: 'Requirements gathering' }
},
{
content: 'Phase 2',
data: { date: 'Feb 2024', description: 'Design and planning' }
}
];
}<ejs-timeline [template]="itemTemplate">
<e-items>
<e-item *ngFor="let item of milestones" [content]="item.content"></e-item>
</e-items>
<ng-template #itemTemplate let-data="">
<div class="timeline-item">
<h4>{{ data.item.content }}</h4>
<p>{{ data.item.data?.date }}</p>
<span>{{ data.item.data?.description }}</span>
</div>
</ng-template>
</ejs-timeline>Use this when: Rich formatting, nested information, images in items, or complex layouts.
Opposite Content
Dual-Sided Display
Display content on both sides of the timeline using oppositeContent:
public frameworks: TimelineItemModel[] = [
{ content: 'ReactJs', oppositeContent: 'Owned by Facebook' },
{ content: 'Angular', oppositeContent: 'Owned by Google' },
{ content: 'VueJs', oppositeContent: 'Owned by Evan you' },
{ content: 'Svelte', oppositeContent: 'Owned by Rich Harris' }
];<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of frameworks"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>Behavior:
- Vertical orientation:
contenton left,oppositeContenton right - Horizontal orientation:
contenton top,oppositeContenton bottom - Works with all alignment values:
Before,After,Alternate,AlternateReverse
Use Cases for Opposite Content
- Comparison timelines: Two parallel information streams
- Before/After scenarios: Original vs updated information
- Timeline with descriptions: Main event + context on opposite side
- Team collaboration: Person A's timeline vs Person B's timeline
CSS Classes for Items
Applying Custom Styles
Add CSS classes to individual items for styling and state management:
public orderStatus: TimelineItemModel[] = [
{ content: 'Ordered', cssClass: 'state-completed' },
{ content: 'Shipped', cssClass: 'state-progress' },
{ content: 'Delivered', cssClass: 'state-pending' },
{ content: 'Returned', cssClass: 'state-cancelled' }
];<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of orderStatus"
[content]="item.content"
[cssClass]="item.cssClass"></e-item>
</e-items>
</ejs-timeline>.state-completed {
background-color: #d4edda;
border-left: 3px solid #28a745;
}
.state-progress {
background-color: #fff3cd;
border-left: 3px solid #ffc107;
}
.state-pending {
background-color: #d1ecf1;
border-left: 3px solid #17a2b8;
}
.state-cancelled {
background-color: #f8d7da;
border-left: 3px solid #dc3545;
}Use this when: Distinguishing item states, visual hierarchy, or status indicators.
Disabled Items
Disabling Timeline Items
Set items as disabled to prevent interaction and apply disabled styling:
public taskList: TimelineItemModel[] = [
{ content: 'Task 1: Analysis', disabled: false },
{ content: 'Task 2: Design', disabled: false },
{ content: 'Task 3: Development', disabled: false },
{ content: 'Task 4: Testing', disabled: true }, // Not yet available
{ content: 'Task 5: Deployment', disabled: true } // Not yet available
];<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of taskList"
[content]="item.content"
[disabled]="item.disabled"></e-item>
</e-items>
</ejs-timeline>Visual effects of `disabled: true`:
- Item appears grayed out by default
- Typically shows reduced opacity
- May have different dot styling
Using Disabled with CSS Classes
Combine disabled state with custom styling:
public phases: TimelineItemModel[] = [
{ content: 'Phase 1: Planning', disabled: false, cssClass: 'phase-active' },
{ content: 'Phase 2: Development', disabled: false, cssClass: 'phase-active' },
{ content: 'Phase 3: Review', disabled: true, cssClass: 'phase-upcoming' },
{ content: 'Phase 4: Production', disabled: true, cssClass: 'phase-upcoming' }
];.phase-active {
color: #333;
}
.phase-upcoming {
color: #999;
opacity: 0.6;
}Use this when: Multi-phase processes, unlocking features progressively, or showing completed vs pending tasks.
Dot Item Property
Customizing Dot Appearance with dotItem
The dotItem property allows per-item dot customization. This is used in conjunction with CSS or the customization.md reference for visual distinction:
public milestones: TimelineItemModel[] = [
{ content: 'Milestone 1', dotItem: { dotIconCss: 'dot-primary' } },
{ content: 'Milestone 2', dotItem: { dotIconCss: 'dot-secondary' } },
{ content: 'Milestone 3', dotItem: { dotIconCss: 'dot-completed' } }
];dotItem properties:
dotIconCss- CSS class for dot icon stylingdotSize- Custom size for the dot (in pixels)
Dot Icons with dotCss
Adding Icons to Timeline Dots
The dotCss property allows you to add custom CSS classes to individual timeline dots. This enables displaying icons, images, or custom styling per item.
dotCss Property:
- Type:
string - Description: CSS class(es) applied to the dot element
- Use Case: Add Material Icons, FontAwesome icons, or custom SVG to dots
Basic Icon Example
export class AppComponent {
public processSteps: TimelineItemModel[] = [
{
content: 'Analysis',
oppositeContent: 'Week 1',
dotCss: 'icon-analysis'
},
{
content: 'Design',
oppositeContent: 'Week 2',
dotCss: 'icon-design'
},
{
content: 'Development',
oppositeContent: 'Week 3-5',
dotCss: 'icon-code'
},
{
content: 'Testing',
oppositeContent: 'Week 6',
dotCss: 'icon-test'
}
];
}<ejs-timeline align="Before">
<e-items>
<e-item *ngFor="let item of processSteps"
[content]="item.content"
[oppositeContent]="item.oppositeContent"
[dotCss]="item.dotCss"></e-item>
</e-items>
</ejs-timeline>/* Material Icons styling */
.e-dot::before {
font-family: 'Material Icons';
font-size: 18px;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
.icon-analysis .e-dot {
background-color: #2196F3;
}
.icon-analysis .e-dot::before {
content: '\\e8f4'; /* analytics icon */
}
.icon-design .e-dot {
background-color: #9C27B0;
}
.icon-design .e-dot::before {
content: '\\e3fd'; /* design_services icon */
}
.icon-code .e-dot {
background-color: #4CAF50;
}
.icon-code .e-dot::before {
content: '\\e86e'; /* code icon */
}
.icon-test .e-dot {
background-color: #FF9800;
}
.icon-test .e-dot::before {
content: '\\e8f0'; /* bug_report icon */
}Combining dotCss with cssClass and disabled
export class AppComponent {
public deploymentStages: TimelineItemModel[] = [
{
content: 'Build',
oppositeContent: 'Stage 1',
dotCss: 'icon-build',
cssClass: 'stage-completed',
disabled: false
},
{
content: 'Staging',
oppositeContent: 'Stage 2',
dotCss: 'icon-staging',
cssClass: 'stage-current',
disabled: false
},
{
content: 'Production',
oppositeContent: 'Stage 3',
dotCss: 'icon-production',
cssClass: 'stage-pending',
disabled: true
}
];
}Status Indicators with Icons
export class AppComponent {
public taskStatus: TimelineItemModel[] = [
{
content: 'Pending',
dotCss: 'status-pending'
},
{
content: 'In Progress',
dotCss: 'status-progress'
},
{
content: 'Completed',
dotCss: 'status-completed'
}
];
}/* Status indicator styles */
.status-pending .e-dot {
background-color: #ECEFF1;
border: 2px solid #90A4AE;
}
.status-progress .e-dot {
background-color: #FFC107;
animation: pulse 2s infinite;
}
.status-progress .e-dot::before {
content: '\\e88a'; /* schedule icon */
animation: spin 1s linear infinite;
}
.status-completed .e-dot {
background-color: #4CAF50;
}
.status-completed .e-dot::before {
content: '\\e5ca'; /* check_circle icon */
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); }
50% { box-shadow: 0 0 0 8px rgba(255, 193, 7, 0); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}For complete dotCss examples with Material Design Icons, FontAwesome, and advanced styling, see [customization.md](customization.md#dot-icons-with-dotcss-property).
Data Binding from Arrays
Complete Data Binding Example
Bind timeline data from component arrays:
import { TimelineItemModel } from '@syncfusion/ej2-angular-layouts';
export class AppComponent {
public projectTimeline: TimelineItemModel[] = [
{
content: 'Project Kickoff',
oppositeContent: 'January 15, 2024',
cssClass: 'milestone-important',
disabled: false
},
{
content: 'Design Phase',
oppositeContent: 'February 1-28, 2024',
cssClass: 'phase-standard',
disabled: false
},
{
content: 'Development',
oppositeContent: 'March 1 - May 31, 2024',
cssClass: 'phase-standard',
disabled: false
},
{
content: 'QA Testing',
oppositeContent: 'June 1-15, 2024',
cssClass: 'phase-review',
disabled: true // Not yet started
},
{
content: 'Launch',
oppositeContent: 'June 20, 2024',
cssClass: 'milestone-important',
disabled: true // Not yet started
}
];
}<ejs-timeline align="Alternate" orientation="Vertical">
<e-items>
<e-item *ngFor="let item of projectTimeline"
[content]="item.content"
[oppositeContent]="item.oppositeContent"
[cssClass]="item.cssClass"
[disabled]="item.disabled"></e-item>
</e-items>
</ejs-timeline>Dynamic Data Updates
Add and update items programmatically:
public addItem() {
const newItem: TimelineItemModel = {
content: 'New Milestone',
oppositeContent: 'Added dynamically',
disabled: false
};
this.projectTimeline.push(newItem);
}
public updateItem(index: number, newContent: string) {
this.projectTimeline[index].content = newContent;
}Fetching Data from API
Load timeline data from a backend service:
import { HttpClient } from '@angular/common/http';
export class AppComponent implements OnInit {
public timeline: TimelineItemModel[] = [];
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get<TimelineItemModel[]>('/api/timeline')
.subscribe(data => {
this.timeline = data;
});
}
}Use this when: Displaying dynamic content, loading data from servers, or managing large datasets.
Orientations and Reverse
Table of Contents
- Vertical Orientation
- Horizontal Orientation
- Reverse Property
- Reverse with Different Alignments
- Use Cases
Vertical Orientation
Overview
Vertical orientation is the default display mode, showing timeline items in a top-to-bottom sequence. The timeline connector runs vertically down the middle, with content positioned on left and/or right sides.
Characteristics
- Direction: Top-to-bottom (chronological flow)
- Default: No need to specify
orientation="Vertical" - Connector: Vertical line in the middle
- Content placement: Left/right sides depending on alignment
- Best for: Traditional chronological timelines
Vertical Example
import { CommonModule } from '@angular/common';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { Component } from '@angular/core';
@Component({
imports: [CommonModule, TimelineModule, TimelineAllModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
public roadmap: TimelineItemModel[] = [
{ content: 'Q1 Planning', oppositeContent: 'January - March 2024' },
{ content: 'Q2 Development', oppositeContent: 'April - June 2024' },
{ content: 'Q3 Testing', oppositeContent: 'July - September 2024' },
{ content: 'Q4 Launch', oppositeContent: 'October - December 2024' }
];
}<div class="container" style="height: 400px;">
<ejs-timeline orientation="Vertical" align="Before">
<e-items>
<e-item *ngFor="let item of roadmap"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Result: Timeline flows from top to bottom with planning on left, dates on right.
When to Use Vertical
- Product release milestones
- Project phases over time
- Educational achievements
- Career progression
- Historical events
- Process workflows
Horizontal Orientation
Overview
Horizontal orientation displays timeline items in a left-to-right sequence. The timeline connector runs horizontally across the middle, with content positioned above and/or below.
Characteristics
- Direction: Left-to-right (chronological flow)
- Connector: Horizontal line in the middle
- Content placement: Top/bottom sides depending on alignment
- Best for: Space-constrained layouts, presentation-style timelines
- Container: Requires adequate horizontal space or scrolling
Horizontal Example
export class AppComponent {
public projectMilestones: TimelineItemModel[] = [
{ content: 'Kickoff', oppositeContent: 'January 2024' },
{ content: 'Design', oppositeContent: 'February 2024' },
{ content: 'Development', oppositeContent: 'March-May 2024' },
{ content: 'Testing', oppositeContent: 'June 2024' },
{ content: 'Launch', oppositeContent: 'July 2024' }
];
}<div class="container" style="width: 100%; overflow-x: auto;">
<div style="min-width: 1200px; height: 250px;">
<ejs-timeline orientation="Horizontal" align="Alternate">
<e-items>
<e-item *ngFor="let item of projectMilestones"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>
</div>Result: Timeline flows left to right with alternating top/bottom content.
When to Use Horizontal
- Presentation slides or storytelling
- Website hero sections
- Process flow diagrams
- Limited vertical space
- Wide-screen layouts
- Customer journey maps
- Step-by-step progression display
Reverse Property
Overview
The reverse property (boolean, default false) reverses the order of timeline items. Latest or endpoint content appears first, creating newest-first displays.
Behavior
When reverse="true":
- Items display in reverse order
- Last item in array appears first visually
- Works with all orientations and alignments
- Useful for activity feeds and recent-first timelines
Basic Reverse Example
export class AppComponent {
public activityLog: TimelineItemModel[] = [
{ content: 'Task 1', oppositeContent: 'January 2024' },
{ content: 'Task 2', oppositeContent: 'February 2024' },
{ content: 'Task 3', oppositeContent: 'March 2024' },
{ content: 'Task 4', oppositeContent: 'April 2024' }
];
}Without reverse:
Timeline displays: Task 1 → Task 2 → Task 3 → Task 4 (chronological)With reverse="true":
Timeline displays: Task 4 → Task 3 → Task 2 → Task 1 (reverse chronological)<ejs-timeline orientation="Vertical" align="Before" [reverse]="true">
<e-items>
<e-item *ngFor="let item of activityLog"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>Activity Feed Example
export class AppComponent {
public recentActivities: TimelineItemModel[] = [
{ content: 'User registration', oppositeContent: 'Jan 1, 2024 - 9:00 AM' },
{ content: 'Email verification', oppositeContent: 'Jan 2, 2024 - 2:30 PM' },
{ content: 'Profile completion', oppositeContent: 'Jan 3, 2024 - 11:15 AM' },
{ content: 'First purchase', oppositeContent: 'Jan 5, 2024 - 3:45 PM' },
{ content: 'Account upgrade', oppositeContent: 'Jan 8, 2024 - 10:20 AM' }
];
}<div class="activity-feed">
<h3>Recent Activity</h3>
<ejs-timeline orientation="Vertical" align="Before" [reverse]="true">
<e-items>
<e-item *ngFor="let activity of recentActivities"
[content]="activity.content"
[oppositeContent]="activity.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Result: Most recent activity (account upgrade) appears at top, older activities below.
Reverse with Different Alignments
Reverse + Before
<ejs-timeline orientation="Vertical" align="Before" [reverse]="true">
<!-- Items display right-to-left in array, content on left, opposite on right -->
</ejs-timeline>Use for: Recent-first left-aligned content.
Reverse + After
<ejs-timeline orientation="Vertical" align="After" [reverse]="true">
<!-- Items display right-to-left in array, content on right, opposite on left -->
</ejs-timeline>Use for: Recent-first right-aligned content.
Reverse + Alternate
export class AppComponent {
public careerProgress: TimelineItemModel[] = [
{ content: 'June 2022', oppositeContent: 'Graduated in Computer Engineering' },
{ content: 'Aug 2022', oppositeContent: 'Software Engineering Internship' },
{ content: 'Feb 2023', oppositeContent: 'Associate Software Engineer' },
{ content: 'Mar 2024', oppositeContent: 'Software Level 1 Engineer' }
];
}<div class="container" style="height: 330px; margin-top: 30px;">
<ejs-timeline orientation="Vertical" align="Alternate" [reverse]="true">
<e-items>
<e-item *ngFor="let item of careerProgress"
[content]="item.content"
[oppositeContent]="item.oppositeContent"></e-item>
</e-items>
</ejs-timeline>
</div>Result: Career starts with most recent position (Mar 2024) at top, proceeding backward to graduation.
Use for: Career timelines showing progression, education history, or any reverse-chronological narrative.
Reverse + Horizontal
export class AppComponent {
public newsFeed: TimelineItemModel[] = [
{ content: 'Breaking News 1', oppositeContent: '1 hour ago' },
{ content: 'Breaking News 2', oppositeContent: '2 hours ago' },
{ content: 'Breaking News 3', oppositeContent: '3 hours ago' }
];
}<ejs-timeline orientation="Horizontal" align="Alternate" [reverse]="true">
<e-items>
<e-item *ngFor="let news of newsFeed"
[content]="news.content"
[oppositeContent]="news.oppositeContent"></e-item>
</e-items>
</ejs-timeline>Use Cases
Vertical Reverse = Activity Feed
Best for: Twitter-like feeds, notification history, log entries
- Most recent at top
- Chronological downward (but newest-first order)
- Easy to scan recent activity
Vertical Reverse = Career/Education Timeline
Best for: Resume timelines, experience highlights, educational progression
- Current position/achievement at top
- Going backward in time naturally
- Modern-to-past narrative flow
Horizontal = Product Launch Roadmap
Best for: Presentation slides, marketing timeline, product evolution
- Left-to-right natural reading direction
- Fits wide screens and presentations
- Good for investor presentations
Horizontal Reverse = News/Updates
Best for: Latest news first, recent announcements, changelog
- Most recent on left (natural reading start)
- Reverse-chronological within left-to-right flow
- Engaging modern presentation
Choosing Orientation
| Use Case | Orientation | Reverse | Rationale |
|---|---|---|---|
| Project timeline | Vertical | No | Chronological top-to-bottom |
| Activity feed | Vertical | Yes | Recent first, familiar pattern |
| News feed | Horizontal | Yes | Modern web pattern |
| Career history | Vertical | Yes | Recent achievement first |
| Roadmap | Vertical | No | Future progression |
| Process steps | Horizontal | No | Left-to-right flow |
Templates
Table of Contents
Overview
The Timeline component provides comprehensive template customization through the template property, allowing you to completely restructure how timeline items appear. Templates give you full control over:
- Dot appearance and icons
- Content layout and styling
- Connector appearance
- Item spacing and structure
- Adding custom HTML and styling
Template Context
Templates have access to:
item- Current Timeline item objectitemIndex- Zero-based index of the item in the array
<ng-template #myTemplate let-data="">
<!-- Access item: data.item -->
<!-- Access index: data.itemIndex -->
</ng-template>Template Basics
Basic Template Example
import { Component } from '@angular/core';
import { TimelineItemModel, TimelineModule, TimelineAllModule } from "@syncfusion/ej2-angular-layouts";
import { CommonModule } from '@angular/common';
@Component({
imports: [TimelineModule, TimelineAllModule, CommonModule],
standalone: true,
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public projectMilestones: TimelineItemModel[] = [
{ content: 'Kickoff meeting' },
{ content: 'Content approved' },
{ content: 'Design approved' },
{ content: 'Product delivered' }
];
}<div class="container" style="margin-top: 50px;">
<ejs-timeline orientation="Horizontal" cssClass="custom-timeline" [template]="timelineTemplate">
<e-items>
<e-item *ngFor="let item of projectMilestones" [content]="item.content"></e-item>
</e-items>
<ng-template #timelineTemplate let-data="">
<div class='template-container item-{{data.itemIndex}}'>
<div class="content-container">
<div class="timeline-content">{{data.item.content}}</div>
</div>
<div class="content-connector"></div>
<div class="progress-line">
<span class="indicator"></span>
</div>
</div>
</ng-template>
</ejs-timeline>
</div>.custom-timeline .template-container {
display: flex;
flex-direction: column;
align-items: center;
}
.custom-timeline .timeline-content {
padding: 12px 20px;
background-color: #2196F3;
color: white;
border-radius: 4px;
font-weight: 500;
white-space: nowrap;
}
.custom-timeline .indicator {
width: 16px;
height: 16px;
background-color: #2196F3;
border: 3px solid white;
border-radius: 50%;
display: inline-block;
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.4);
}Custom Dot Design
Icon-Based Dots
export class AppComponent {
public workflow: TimelineItemModel[] = [
{ content: 'Submitted', icon: '📝', status: 'completed' },
{ content: 'In Review', icon: '👀', status: 'progress' },
{ content: 'Approved', icon: '✓', status: 'pending' },
{ content: 'Published', icon: '🚀', status: 'pending' }
];
}<ejs-timeline [template]="dotTemplate" align="Before">
<e-items>
<e-item *ngFor="let item of workflow"
[content]="item.content"
[cssClass]="'status-' + item.status"></e-item>
</e-items>
<ng-template #dotTemplate let-data="">
<div class="custom-dot {{data.item.status}}">
<span class="dot-icon">{{data.item.icon}}</span>
</div>
<div class="dot-content">
<div class="dot-title">{{data.item.content}}</div>
<div class="dot-status">{{data.item.status}}</div>
</div>
</ng-template>
</ejs-timeline>.custom-dot {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
margin-bottom: 10px;
}
.custom-dot.completed {
background-color: #4CAF50;
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.4);
}
.custom-dot.progress {
background-color: #2196F3;
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4);
animation: pulse-dot 2s infinite;
}
.custom-dot.pending {
background-color: #BDBDBD;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.dot-content {
text-align: center;
}
.dot-title {
font-weight: 600;
font-size: 14px;
margin: 5px 0;
}
.dot-status {
font-size: 12px;
color: #666;
text-transform: capitalize;
}
@keyframes pulse-dot {
0%, 100% { box-shadow: 0 0 0 0 rgba(33, 150, 243, 0.7); }
50% { box-shadow: 0 0 0 10px rgba(33, 150, 243, 0); }
}Progress Indicator Dots
export class AppComponent {
public releases: TimelineItemModel[] = [
{ content: 'v1.0.0', progress: 100 },
{ content: 'v1.1.0', progress: 100 },
{ content: 'v2.0.0', progress: 75 },
{ content: 'v2.1.0 (Beta)', progress: 40 }
];
}<ejs-timeline [template]="progressTemplate" align="Before">
<e-items>
<e-item *ngFor="let item of releases" [content]="item.content"></e-item>
</e-items>
<ng-template #progressTemplate let-data="">
<div class="progress-dot">
<svg width="80" height="80" viewBox="0 0 80 80">
<circle cx="40" cy="40" r="35" class="progress-bg" />
<circle cx="40" cy="40" r="35" class="progress-fill"
[style.strokeDasharray]="'220 * ' + data.item.progress / 100 + ', 220'"/>
<text x="40" y="45" class="progress-text">{{data.item.progress}}%</text>
</svg>
<div class="progress-label">{{data.item.content}}</div>
</div>
</ng-template>
</ejs-timeline>.progress-dot svg {
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
}
.progress-bg {
fill: none;
stroke: #E0E0E0;
stroke-width: 4;
}
.progress-fill {
fill: none;
stroke: #2196F3;
stroke-width: 4;
stroke-linecap: round;
transform-origin: 40px 40px;
transform: rotate(-90deg);
transition: stroke-dasharray 0.3s ease;
}
.progress-text {
text-anchor: middle;
font-size: 12px;
font-weight: bold;
fill: #333;
}
.progress-label {
text-align: center;
margin-top: 8px;
font-size: 12px;
color: #666;
}Content Area Customization
Rich Content Template
export class AppComponent {
public events: TimelineItemModel[] = [
{
content: 'Conference',
date: 'March 15, 2024',
location: 'San Francisco',
attendees: 150,
image: 'assets/conference.jpg'
},
{
content: 'Workshop',
date: 'April 20, 2024',
location: 'New York',
attendees: 50,
image: 'assets/workshop.jpg'
}
];
}<ejs-timeline [template]="richTemplate" align="Before" orientation="Vertical">
<e-items>
<e-item *ngFor="let item of events" [content]="item.content"></e-item>
</e-items>
<ng-template #richTemplate let-data="">
<div class="event-card">
<div class="event-image">
<img [src]="data.item.image" [alt]="data.item.content" />
</div>
<div class="event-details">
<h3>{{data.item.content}}</h3>
<p class="event-date">📅 {{data.item.date}}</p>
<p class="event-location">📍 {{data.item.location}}</p>
<p class="event-attendees">👥 {{data.item.attendees}} attendees</p>
</div>
</div>
</ng-template>
</ejs-timeline>.event-card {
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
max-width: 300px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.event-card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.event-image {
width: 100%;
height: 180px;
overflow: hidden;
}
.event-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.event-details {
padding: 16px;
}
.event-details h3 {
margin: 0 0 10px 0;
font-size: 16px;
color: #333;
}
.event-details p {
margin: 6px 0;
font-size: 13px;
color: #666;
}Complex Layout Examples
Educational Timeline with Descriptions
export class AppComponent {
public curriculum: TimelineItemModel[] = [
{
content: 'Semester 1',
courses: ['Math 101', 'Physics 101', 'Chemistry 101'],
gpa: 3.8
},
{
content: 'Semester 2',
courses: ['Calculus', 'Physics 102', 'Biology'],
gpa: 3.9
},
{
content: 'Semester 3',
courses: ['Linear Algebra', 'Advanced Physics', 'Organic Chemistry'],
gpa: 3.7
}
];
}<ejs-timeline [template]="semesterTemplate" align="Alternate" orientation="Vertical">
<e-items>
<e-item *ngFor="let item of curriculum" [content]="item.content"></e-item>
</e-items>
<ng-template #semesterTemplate let-data="">
<div class="semester-card">
<div class="semester-header">
<h4>{{data.item.content}}</h4>
<span class="gpa-badge">GPA: {{data.item.gpa}}</span>
</div>
<div class="courses-list">
<div class="course" *ngFor="let course of data.item.courses">
<span class="course-bullet">•</span>
<span>{{course}}</span>
</div>
</div>
</div>
</ng-template>
</ejs-timeline>.semester-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 8px;
padding: 16px;
min-width: 250px;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.semester-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.semester-header h4 {
margin: 0;
font-size: 16px;
}
.gpa-badge {
background: rgba(255, 255, 255, 0.3);
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.courses-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.course {
display: flex;
align-items: center;
font-size: 13px;
}
.course-bullet {
margin-right: 8px;
font-weight: bold;
}Status Timeline with Progress
export class AppComponent {
public deploymentSteps: TimelineItemModel[] = [
{ step: 'Build', status: 'completed', duration: '2m 34s' },
{ step: 'Test', status: 'completed', duration: '5m 12s' },
{ step: 'Deploy', status: 'progress', duration: 'Running...' },
{ step: 'Verify', status: 'pending', duration: 'Waiting' }
];
}<ejs-timeline [template]="deployTemplate" align="Before">
<e-items>
<e-item *ngFor="let item of deploymentSteps" [content]="item.step"></e-item>
</e-items>
<ng-template #deployTemplate let-data="">
<div class="deploy-step" [class]="'status-' + data.item.status">
<div class="step-number">{{data.itemIndex + 1}}</div>
<div class="step-info">
<div class="step-name">{{data.item.step}}</div>
<div class="step-duration">{{data.item.duration}}</div>
</div>
<div class="step-status">
<span *ngIf="data.item.status === 'completed'" class="badge-success">✓</span>
<span *ngIf="data.item.status === 'progress'" class="badge-progress">⟳</span>
<span *ngIf="data.item.status === 'pending'" class="badge-pending">⋯</span>
</div>
</div>
</ng-template>
</ejs-timeline>.deploy-step {
display: flex;
align-items: center;
padding: 12px 16px;
background: #F5F5F5;
border-radius: 6px;
gap: 12px;
border-left: 3px solid #CCC;
}
.deploy-step.status-completed {
background: #E8F5E9;
border-left-color: #4CAF50;
}
.deploy-step.status-progress {
background: #E3F2FD;
border-left-color: #2196F3;
}
.deploy-step.status-pending {
background: #FFF3E0;
border-left-color: #FF9800;
}
.step-number {
width: 32px;
height: 32px;
background: #669;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 14px;
}
.step-info {
flex: 1;
}
.step-name {
font-weight: 600;
font-size: 14px;
color: #333;
}
.step-duration {
font-size: 12px;
color: #999;
margin-top: 2px;
}
.step-status {
font-size: 18px;
}
.badge-success { color: #4CAF50; }
.badge-progress { color: #2196F3; animation: spin 2s linear infinite; }
.badge-pending { color: #FF9800; }
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}