YouTube Playlist Carousel

The YouTube Playlist Carousel displays playlist videos in a carousel. Use server-side rendering: pass a videos array so all slides are rendered at build time with no API call.

Pre-rendered (no API call)

Pass a videos array to render all slides when the page loads. No loading state and no API call.

Server-side rendering markup

Pass a videos array so all slides are rendered at build time. The output includes data-videos-embedded="true" so the client does not call the API.

<div class="lta-youtube-playlist-carousel js-lta-youtube-playlist-carousel" data-videos-embedded="true">
	<div class="lta-youtube-playlist-carousel__loading" style="display: none;" aria-hidden="true">
		<p>Loading videos...</p>
	</div>
	<div class="lta-youtube-playlist-carousel__content">
		<div class="lta-media-carousel lta-youtube-playlist-carousel__carousel" data-loop="false">
			<div class="swiper">
				<div class="swiper-wrapper">
					<!-- One .swiper-slide per video (data-is-video="true"), each with figure.lta-video-embed, lite-video, figcaption -->
					<div class="swiper-slide" data-is-video="true">
						<figure class="lta-video-embed" data-video-name="Video title" data-video-url="https://www.youtube.com/watch?v=VIDEO_ID" data-video-date="...">
							<lite-video videotype="youtube" videoid="VIDEO_ID" fallback-thumbnail="https://i.ytimg.com/vi/VIDEO_ID/maxresdefault.jpg"></lite-video>
							<div class="lta-video-embed__caption-backdrop u-rounded-b"></div>
							<figcaption class="lta-video-embed__caption u-rounded-b"><span>Video title | Monday, 20 September 2025</span></figcaption>
						</figure>
					</div>
					<!-- ... more .swiper-slide -->
				</div>
				<div class="lta-media-carousel__controls">...</div>
			</div>
		</div>
	</div>
	<div class="lta-youtube-playlist-carousel__error" style="display: none;" aria-hidden="true">
		<p class="u-text-error">Failed to load videos. Please try again later.</p>
	</div>
</div>

In 11ty, use the component with a videos array (e.g. from frontmatter or a data file) so this markup is generated at build time. No API key or runtime fetch is required.

Props

PropTypeRequiredDefaultDescription
videosarrayYes-Array of video objects for server-side rendering: videoId, title, thumbnail, publishedAt, videoUrl, description. All slides are rendered at build time (no API call).
classesstringNo-Additional CSS classes to apply to the component wrapper

Configuration

When using server-side rendering with the videos prop, all slides are output at build time. No API key or runtime request is required; the client only initialises the carousel on the existing markup.

With blockId (API fetch)

Pass a blockId as the payload. The client calls /api/youtube/block?blockId=...; the backend returns playlist videos for that block. The server outputs a shell with loading state; the client fetches and injects the slides.

Server-side rendering markup (blockId)

When blockId is provided, the server renders this markup. The client reads data-block-id, calls GET /api/youtube/block?blockId=..., then injects slides and shows the content.

<div class="lta-youtube-playlist-carousel js-lta-youtube-playlist-carousel" data-block-id="youtube-playlist-bjk" data-cache-duration="10" data-block-endpoint="/api/youtube/block">
   <div class="lta-youtube-playlist-carousel__loading u-text-center u-py-4">
      <p>Loading videos...</p>
   </div>
   <div class="lta-youtube-playlist-carousel__content" style="display: none;">
      <div class="lta-media-carousel lta-youtube-playlist-carousel__carousel" data-loop="false">
         <div class="swiper">
            <div class="swiper-wrapper"></div>
            <div class="lta-media-carousel__controls">
               <button class="lta-media-carousel__prev u-mr-2">
                  <svg class="lta-icon js-lta-text-input--btn-icon" width="2rem" height="2rem" style="color:currentColor;" fill="currentColor" alt="arrow-left-circle" aria-hidden="true">
                     <use xlink:href="/static/images/icons/lta-icons.svg#arrow-left-circle"></use>
                  </svg>
               </button>
               <div class="swiper-pagination u-hstack u-gap-1"></div>
               <button class="lta-media-carousel__next u-ml-2">
                  <svg class="lta-icon js-lta-text-input--btn-icon" width="2rem" height="2rem" style="color:currentColor;" fill="currentColor" alt="arrow-right-circle" aria-hidden="true">
                     <use xlink:href="/static/images/icons/lta-icons.svg#arrow-right-circle"></use>
                  </svg>
               </button>
            </div>
         </div>
      </div>
   </div>
   <div class="lta-youtube-playlist-carousel__error" style="display: none;">
      <p class="u-text-error">Failed to load videos. Please try again later.</p>
   </div>
</div>

Params (blockId mode)

ParamTypeRequiredDefaultDescription
blockIdstringYes-Block identifier. The client calls /api/youtube/block with this id; the backend returns playlist videos for the block.
cacheDurationstring/numberNo-Optional. Passed as data-cache-duration and to the block API (e.g. cache duration in minutes).
blockEndpointstringNo/api/youtube/blockBlock API URL. Passed as data-block-endpoint; the client uses it when fetching. Omit to use the default.