Server-Side Rendering
Server-Side Rendering (SSR)
Embla Carousel is designed to be server-side rendering friendly. Because it relies on standard CSS for its layout engine, you can achieve a seamless user experience with zero Cumulative Layout Shift (CLS) by following a few best practices.
Preventing Layout Shifts
The most common issue with carousels in SSR environments (like Next.js, Nuxt, or SvelteKit) is the layout jumping once the JavaScript hydrates. This happens if the carousel slides haven't been assigned a width via CSS before Embla initializes.
To prevent this, you should ensure your slide styles are available in the initial HTML payload. Embla does not inject layout styles automatically; it expects your CSS to define how many slides are visible.
/* Example: 3 slides visible on all platforms */
.embla__container {
display: flex;
}
.embla__slide {
flex: 0 0 33.3333%;
min-width: 0;
}
The ssr Option
When using Embla in an SSR context, the library needs to know which slides should be considered "active" or "selected" before it has access to the DOM. This is handled via the ssr option.
The ssr option allows you to provide an object that mimics the initial state of the carousel. This ensures that the generated markup and logic match between the server and the client.
API Definition
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| ssr | SsrOptionsType | undefined | Configuration for server-side rendering behavior. |
Framework-Specific Implementation
Next.js (React)
When using useEmblaCarousel in Next.js, the hook handles hydration automatically. However, you can pass the ssr option to ensure the initial scroll position is correct.
import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() {
const [emblaRef] = useEmblaCarousel({
loop: true,
// Add SSR support if starting at a specific index
startIndex: 1,
})
return (
<div className="embla" ref={emblaRef}>
<div className="embla__container">
<div className="embla__slide">Slide 1</div>
<div className="embla__slide">Slide 2</div>
<div className="embla__slide">Slide 3</div>
</div>
</div>
)
}
Nuxt (Vue) or SvelteKit
In Vue and Svelte, the principle remains the same. Use the framework-specific packages (embla-carousel-vue or embla-carousel-svelte) and prioritize CSS-based widths.
Hydration Strategy
Embla Carousel initializes as soon as the component mounts on the client. During the "Server-to-Client" transition:
- Server: Renders the HTML structure with your provided CSS.
- Browser (Pre-hydration): The user sees the carousel in its CSS-defined state. If you defined
.embla__slideto be100%width, the user sees one slide. - Browser (Post-hydration): Embla's engine kicks in, calculates the precise offsets, and enables drag/swipe interactions.
Best Practices for SSR
- Avoid "Display: None": Do not hide the carousel container during hydration. This prevents Embla from calculating node offsets correctly when it wakes up.
- Aspect Ratio Boxes: Use the CSS
aspect-ratioproperty or padding-top hacks on your slides to reserve vertical space before images load. - Media Queries: If you change the number of visible slides at different breakpoints, use standard CSS media queries rather than JavaScript-based window width checks. Embla will pick up the CSS changes automatically on resize.
/* Responsive slides: 1 on mobile, 3 on desktop */
.embla__slide {
flex: 0 0 100%;
}
@media (min-width: 768px) {
.embla__slide {
flex: 0 0 33.33%;
}
}