YouTube Player
YouTube hosts videos and provides an iframe player API.
Nuxt Scripts provides a useScriptYouTubePlayer() composable and a headless <ScriptYouTubePlayer> component for controlling the YouTube player.
Nuxt Config Setup
Add this to your nuxt.config.ts to load YouTube Player globally. Alternatively you can use the useScriptYouTubePlayer composable for more control.
export default defineNuxtConfig({
scripts: {
registry: {
youtubePlayer: {
trigger: 'onNuxtReady',
}
}
}
})This config automatically enables first-party mode (bundle + proxy). See below to customise.
useScriptYouTubePlayer()
The useScriptYouTubePlayer composable lets you have fine-grain control over when and how YouTube Player is loaded on your site.
const { proxy } = useScriptYouTubePlayer()
const { YT } = await proxyPlease follow the Registry Scripts guide to learn more about advanced usage.
First-Party Mode: Privacy Focused Proxy
No extra config needed. The script is bundled from your domain (faster loads, no extra DNS lookup) and runtime requests are reverse-proxied through your server with automatic anonymisation (user IPs stay hidden from YouTube Player, works with ad blockers). Learn more.
export default defineNuxtConfig({
scripts: {
// ✅ First-party mode: bundled + proxied
registry: {
youtubePlayer: {
trigger: 'onNuxtReady',
},
},
},
})Example
Using YouTube Player in a component with the proxy to send events .
<script setup lang="ts">
const { proxy } = useScriptYouTubePlayer()
// noop in development, ssr
// just works in production, client
function handleAction() {
const { YT } = await proxy
}
</script>
<template>
<div>
<button @click="handleAction">
Send Event
</button>
</div>
</template>Types
Install @types/youtube for full TypeScript support.
pnpm add -D @types/youtube
<ScriptYouTubePlayer>
<ScriptYouTubePlayer> wraps useScriptYouTubePlayer() with a lazy thumbnail and a headless player UI.
An Element Event Trigger delays the iframe API and player until the configured event fires.
The default event is mousedown.
Demo

Privacy
The <ScriptYouTubePlayer> component uses YouTube's privacy-enhanced https://www.youtube-nocookie.com host by default. See YouTube's embed instructions for details.
To use the standard cookie-enabled host, set the cookies prop.
<ScriptYouTubePlayer video-id="d_IFKP1Ofq0" cookies />
Placeholder
The YouTube Player placeholder is a 1280x720 WebP image that is lazy-loaded by default.
Set thumbnailSize to change the placeholder size. Set webp to false for a JPEG thumbnail.
<ScriptYouTubePlayer video-id="d_IFKP1Ofq0" thumbnail-size="maxresdefault" />
For finer control, set placeholderAttrs or replace the image through the #placeholder slot.
Eager Loading
For an above-the-fold video, load the thumbnail eagerly or replace it through the #placeholder slot.
<ScriptYouTubePlayer above-the-fold />
Component API
See the Facade Component API for full props, events, and slots.
Events
The component forwards the six events below. At runtime, each handler receives only the event object shown here. A failure while loading the iframe API also emits error with no arguments. The component's current TypeScript declaration lists a second YT.Player argument for five events, but that argument is not emitted. The YouTube API defines onAutoplayBlocked too, but the component does not currently forward it. See Player Events for payload details.
const emits = defineEmits<{
'ready': [e: YT.PlayerEvent]
'state-change': [e: YT.OnStateChangeEvent]
'playback-quality-change': [e: YT.OnPlaybackQualityChangeEvent]
'playback-rate-change': [e: YT.OnPlaybackRateChangeEvent]
'error': [e: YT.OnErrorEvent]
'api-change': [e: YT.PlayerEvent]
}>()
Slots
Use the slots to control the facade around the player.
default
Always visible.
<template>
<ScriptYouTubePlayer video-id="d_IFKP1Ofq0">
<div class="bg-blue-500 text-white p-5">
Video by Nuxt
</div>
</ScriptYouTubePlayer>
</template>
awaitingLoad
Shown while the component waits for its element trigger.
<template>
<ScriptYouTubePlayer video-id="d_IFKP1Ofq0">
<template #awaitingLoad>
<div class="bg-blue-500 text-white p-5">
Click to play!
</div>
</template>
</ScriptYouTubePlayer>
</template>
loading
Shown while the iframe API loads.
<template>
<ScriptYouTubePlayer video-id="d_IFKP1Ofq0">
<template #loading>
<div class="bg-blue-500 text-white p-5">
Loading...
</div>
</template>
</ScriptYouTubePlayer>
</template>
placeholder
Replaces the default YouTube thumbnail. The slot receives the computed placeholder URL.
<template>
<ScriptYouTubePlayer video-id="d_IFKP1Ofq0">
<template #placeholder="{ placeholder }">
<img :src="placeholder" alt="Video Placeholder">
</template>
</ScriptYouTubePlayer>
</template>
useScriptYouTubePlayer()
Use useScriptYouTubePlayer() when you need to load the iframe API and create a player programmatically.
export function useScriptYouTubePlayer<T extends YouTubePlayerApi>(_options: YouTubePlayerInput) {}
For triggers, proxying, and other script options, see Registry Scripts.
export interface YouTubePlayerApi {
YT: MaybePromise<{
Player: YT.Player
PlayerState: YT.PlayerState
get: (k: string) => any
loaded: 0 | 1
loading: 0 | 1
ready: (f: () => void) => void
scan: () => void
setConfig: (config: YT.PlayerOptions) => void
subscribe: <EventName extends keyof YT.Events>(
event: EventName,
listener: YT.Events[EventName],
context?: any,
) => void
unsubscribe: <EventName extends keyof YT.Events>(
event: EventName,
listener: YT.Events[EventName],
context?: any,
) => void
}>
}Example
Loading the YouTube Player SDK and interacting with it programmatically.
<script setup lang="ts">
const video = ref()
const { onLoaded } = useScriptYouTubePlayer({})
const player = ref(null)
onLoaded(async ({ YT }) => {
// we need to wait for the internal YouTube APIs to be ready
const YouTube = await YT
await new Promise<void>((resolve) => {
if (typeof YouTube.Player === 'undefined')
YouTube.ready(resolve)
else
resolve()
})
// load the API
player.value = new YouTube.Player(video.value, {
videoId: 'd_IFKP1Ofq0'
})
})
function play() {
player.value?.playVideo()
}
</script>
<template>
<div>
<div ref="video" />
<button @click="play">
Play
</button>
</div>
</template>