Use Carousel with CarouselItem children to create a horizontally scrollable showcase.
import { Carousel, CarouselItem } from '@udixio/ui-react';
export default function CarouselBasicReact() {
return (
<Carousel variant="hero" gap={8}>
<CarouselItem>
<div className="p-6 bg-surface rounded-xl">Slide 1</div>
</CarouselItem>
<CarouselItem>
<div className="p-6 bg-surface rounded-xl">Slide 2</div>
</CarouselItem>
<CarouselItem>
<div className="p-6 bg-surface rounded-xl">Slide 3</div>
</CarouselItem>
</Carousel>
);
}import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Carousel, CarouselItem } from '@udixio/ui-angular';
@Component({
selector: 'docs-carousel-basic-angular',
standalone: true,
imports: [Carousel, CarouselItem],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<udx-carousel variant="hero" [gap]="8">
<udx-carousel-item>
<div class="p-6 bg-surface rounded-xl">Slide 1</div>
</udx-carousel-item>
<udx-carousel-item>
<div class="p-6 bg-surface rounded-xl">Slide 2</div>
</udx-carousel-item>
<udx-carousel-item>
<div class="p-6 bg-surface rounded-xl">Slide 3</div>
</udx-carousel-item>
</udx-carousel>
`,
})
export class CarouselBasicAngular {}<script lang="ts">
import { Carousel, CarouselItem } from '@udixio/ui-svelte';
</script>
<Carousel variant="hero" gap={8} accessibleLabel="Featured slides">
<CarouselItem><div class="rounded-xl bg-surface p-6">Slide 1</div></CarouselItem>
<CarouselItem><div class="rounded-xl bg-surface p-6">Slide 2</div></CarouselItem>
<CarouselItem><div class="rounded-xl bg-surface p-6">Slide 3</div></CarouselItem>
</Carousel>Behavior and callbacks
gap: spacing between items.scrollSensitivity: adjust scroll/drag responsiveness.defaultIndex: initial centered index for uncontrolled usage.onIndexChange: called with the accepted centered-index transition.
Svelte uses bind:index for controlled selection and keeps CarouselItem content in the component’s children snippet.
import { Carousel, CarouselItem } from '@udixio/ui-react';
const slides = Array.from({ length: 15 }, (_, i) => i + 1).map((id) => {
const fmt = id % 3;
return {
id,
fmt,
width: fmt === 0 ? 640 : fmt === 1 ? 400 : 240,
height: fmt === 0 ? 240 : fmt === 1 ? 400 : 640,
};
});
export default function CarouselGalleryReact() {
return (
<Carousel
variant="hero"
gap={16}
scrollSensitivity={0.8}
onIndexChange={(index) => console.log('Active slide:', index)}
>
{slides.map(({ id, fmt, width, height }) => (
<CarouselItem key={id}>
<div className="bg-surface rounded-xl h-full flex flex-col">
<div className="flex-1 min-h-0">
<img
className="size-full object-cover rounded-2xl"
src={`https://picsum.photos/seed/udx-${id}-fmt-${fmt}/${width}/${height}`}
alt="Cover"
/>
</div>
<p className="text-title-large m-8 text-nowrap">Slide {id}</p>
</div>
</CarouselItem>
))}
</Carousel>
);
}import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Carousel, CarouselItem } from '@udixio/ui-angular';
const slides = Array.from({ length: 15 }, (_, i) => i + 1).map((id) => {
const fmt = id % 3;
return {
id,
fmt,
width: fmt === 0 ? 640 : fmt === 1 ? 400 : 240,
height: fmt === 0 ? 240 : fmt === 1 ? 400 : 640,
};
});
@Component({
selector: 'docs-carousel-gallery-angular',
standalone: true,
imports: [Carousel, CarouselItem],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<udx-carousel
variant="hero"
[gap]="16"
[scrollSensitivity]="0.8"
(indexChange)="logIndex($event)"
>
@for (slide of slides; track slide.id) {
<udx-carousel-item>
<div class="bg-surface rounded-xl h-full flex flex-col">
<div class="flex-1 min-h-0">
<img
class="size-full object-cover rounded-2xl"
[src]="
'https://picsum.photos/seed/udx-' +
slide.id +
'-fmt-' +
slide.fmt +
'/' +
slide.width +
'/' +
slide.height
"
alt="Cover"
/>
</div>
<p class="text-title-large m-8 text-nowrap">Slide {{ slide.id }}</p>
</div>
</udx-carousel-item>
}
</udx-carousel>
`,
})
export class CarouselGalleryAngular {
protected readonly slides = slides;
protected logIndex(index: number): void {
console.log('Active slide:', index);
}
}<script lang="ts">
import { Carousel, CarouselItem } from '@udixio/ui-svelte';
const slides = Array.from({ length: 15 }, (_, index) => index + 1).map((id) => {
const fmt = id % 3;
return { id, fmt, width: fmt === 0 ? 640 : fmt === 1 ? 400 : 240, height: fmt === 0 ? 240 : fmt === 1 ? 400 : 640 };
});
</script>
<Carousel variant="hero" gap={16} scrollSensitivity={0.8} onIndexChange={(index) => console.log('Active slide:', index)} accessibleLabel="Gallery">
{#each slides as slide (slide.id)}
<CarouselItem>
<div class="flex h-full flex-col rounded-xl bg-surface">
<div class="min-h-0 flex-1"><img class="size-full rounded-2xl object-cover" src={`https://picsum.photos/seed/udx-${slide.id}-fmt-${slide.fmt}/${slide.width}/${slide.height}`} alt="Cover" /></div>
<p class="m-8 text-title-large text-nowrap">Slide {slide.id}</p>
</div>
</CarouselItem>
{/each}
</Carousel>With navigation buttons
onMetricsChange/metricsChange reports the currently visible item count, so external controls can step by a sensible amount and stay in sync with the carousel’s own scroll position. Svelte receives the same metrics object through onMetricsChange.
Visible ≈ 0.00 (full 0), step: 1
import { useState } from 'react';
import {
Button,
Carousel,
CarouselItem,
type CarouselMetrics,
} from '@udixio/ui-react';
const total = 15;
const slides = Array.from({ length: total }, (_, i) => i + 1).map((id) => {
const fmt = id % 3;
return {
id,
fmt,
width: fmt === 0 ? 640 : fmt === 1 ? 400 : 240,
height: fmt === 0 ? 240 : fmt === 1 ? 400 : 640,
};
});
export default function CarouselNavigationReact() {
const [index, setIndex] = useState(0);
const [step, setStep] = useState(1);
const [visible, setVisible] = useState({ approx: 0, full: 0 });
const prev = () => setIndex((i) => Math.max(0, i - step));
const next = () => setIndex((i) => Math.min(total - 1, i + step));
const handleMetricsChange = (metrics: CarouselMetrics) => {
setStep(metrics.stepHalf); // step equals half of the visible items
setVisible({ approx: metrics.visibleApprox, full: metrics.visibleFull });
};
return (
<div className="w-full">
<Carousel
variant="hero"
scrollSensitivity={0.8}
index={index}
onIndexChange={setIndex}
onMetricsChange={handleMetricsChange}
>
{slides.map(({ id, fmt, width, height }) => (
<CarouselItem key={id}>
<div className="bg-surface rounded-xl h-full flex flex-col">
<div className="flex-1 min-h-0">
<img
className="size-full object-cover rounded-2xl"
src={`https://picsum.photos/seed/udx-${id}-fmt-${fmt}/${width}/${height}`}
alt="Cover"
/>
</div>
<p className="text-title-large m-8 text-nowrap">Slide {id}</p>
</div>
</CarouselItem>
))}
</Carousel>
<div className="w-full mt-3 flex items-center justify-between gap-3">
<div className="text-on-surface-variant text-body-small">
Visible ≈ {visible.approx.toFixed(2)} (full {visible.full}), step: {step}
</div>
<div className="flex gap-3">
<Button variant="text" label="Previous" onClick={prev} />
<Button variant="filled" label="Next" onClick={next} />
</div>
</div>
</div>
);
}Visible ≈ 0.00 (full 0), step: 1
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { Button, Carousel, CarouselItem } from '@udixio/ui-angular';
import type { CarouselMetrics } from '@udixio/core';
const total = 15;
const slides = Array.from({ length: total }, (_, i) => i + 1).map((id) => {
const fmt = id % 3;
return {
id,
fmt,
width: fmt === 0 ? 640 : fmt === 1 ? 400 : 240,
height: fmt === 0 ? 240 : fmt === 1 ? 400 : 640,
};
});
@Component({
selector: 'docs-carousel-navigation-angular',
standalone: true,
imports: [Button, Carousel, CarouselItem],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="w-full">
<udx-carousel
variant="hero"
[scrollSensitivity]="0.8"
[index]="index()"
(indexChange)="index.set($event)"
(metricsChange)="handleMetricsChange($event)"
>
@for (slide of slides; track slide.id) {
<udx-carousel-item>
<div class="bg-surface rounded-xl h-full flex flex-col">
<div class="flex-1 min-h-0">
<img
class="size-full object-cover rounded-2xl"
[src]="
'https://picsum.photos/seed/udx-' +
slide.id +
'-fmt-' +
slide.fmt +
'/' +
slide.width +
'/' +
slide.height
"
alt="Cover"
/>
</div>
<p class="text-title-large m-8 text-nowrap">Slide {{ slide.id }}</p>
</div>
</udx-carousel-item>
}
</udx-carousel>
<div class="w-full mt-3 flex items-center justify-between gap-3">
<div class="text-on-surface-variant text-body-small">
Visible ≈ {{ visible().approx.toFixed(2) }} (full {{ visible().full }}), step:
{{ step() }}
</div>
<div class="flex gap-3">
<udx-button variant="text" label="Previous" (click)="prev()" />
<udx-button variant="filled" label="Next" (click)="next()" />
</div>
</div>
</div>
`,
})
export class CarouselNavigationAngular {
protected readonly slides = slides;
protected readonly index = signal(0);
protected readonly step = signal(1);
protected readonly visible = signal({ approx: 0, full: 0 });
protected prev(): void {
this.index.set(Math.max(0, this.index() - this.step()));
}
protected next(): void {
this.index.set(Math.min(total - 1, this.index() + this.step()));
}
protected handleMetricsChange(metrics: CarouselMetrics): void {
this.step.set(metrics.stepHalf); // step equals half of the visible items
this.visible.set({ approx: metrics.visibleApprox, full: metrics.visibleFull });
}
}Visible ≈ 0.00 (full 0), step: 1
<script lang="ts">
import type { CarouselMetrics } from '@udixio/core';
import { Button, Carousel, CarouselItem } from '@udixio/ui-svelte';
const total = 15;
const slides = Array.from({ length: total }, (_, index) => index + 1).map((id) => {
const fmt = id % 3;
return { id, fmt, width: fmt === 0 ? 640 : fmt === 1 ? 400 : 240, height: fmt === 0 ? 240 : fmt === 1 ? 400 : 640 };
});
let index = $state(0);
let step = $state(1);
let visible = $state({ approx: 0, full: 0 });
const handleMetricsChange = (metrics: CarouselMetrics) => {
step = metrics.stepHalf;
visible = { approx: metrics.visibleApprox, full: metrics.visibleFull };
};
</script>
<div class="w-full">
<Carousel bind:index variant="hero" scrollSensitivity={0.8} onMetricsChange={handleMetricsChange} accessibleLabel="Gallery with navigation">
{#each slides as slide (slide.id)}
<CarouselItem>
<div class="flex h-full flex-col rounded-xl bg-surface">
<div class="min-h-0 flex-1"><img class="size-full rounded-2xl object-cover" src={`https://picsum.photos/seed/udx-${slide.id}-fmt-${slide.fmt}/${slide.width}/${slide.height}`} alt="Cover" /></div>
<p class="m-8 text-title-large text-nowrap">Slide {slide.id}</p>
</div>
</CarouselItem>
{/each}
</Carousel>
<div class="mt-3 flex w-full items-center justify-between gap-3">
<div class="text-body-small text-on-surface-variant">Visible ≈ {visible.approx.toFixed(2)} (full {visible.full}), step: {step}</div>
<div class="flex gap-3">
<Button variant="text" label="Previous" onclick={() => (index = Math.max(0, index - step))} />
<Button variant="filled" label="Next" onclick={() => (index = Math.min(total - 1, index + step))} />
</div>
</div>
</div>