Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@u-wave/react-youtube

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@u-wave/react-youtube - npm Package Compare versions

Comparing version 1.0.0-alpha.3 to 1.0.0-alpha.4

5

CHANGELOG.md

@@ -7,2 +7,7 @@ # @u-wave/react-youtube change log

## 1.0.0-alpha.4 - 2022-05-03
* Add docs for the `useYouTube` hook.
* Add props for `origin` / `host` settings.
* Pass-through `muted` to the player initially, so `<YouTube autoplay muted />` works as expected.
## 1.0.0-alpha.3 - 2022-05-01

@@ -9,0 +14,0 @@ * Fix unmount order.

163

dist/react-youtube.js

@@ -13,3 +13,3 @@ 'use strict';

/* global window, document */
/* global YT, document */
function loadSdk() {

@@ -24,3 +24,3 @@ return new Promise((resolve, reject) => {

script.onload = null;
window.YT.ready(resolve);
YT.ready(resolve);
};

@@ -39,6 +39,8 @@

let sdk = null;
/** @param {(sdk: typeof YT) => void} callback */
function getSdk(callback) {
if (typeof window.YT === 'object' && typeof window.YT.ready === 'function') {
if (typeof YT === 'object' && typeof YT.ready === 'function') {
// A YouTube SDK is already loaded, so reuse that
window.YT.ready(callback);
YT.ready(callback);
return;

@@ -58,2 +60,9 @@ }

} = React__default["default"];
const useLayoutEffect = typeof document !== 'undefined' ? React__default["default"].useLayoutEffect : useEffect; // Player state numbers are documented to be constants, so we can inline them.
const ENDED = 0;
const PLAYING = 1;
const PAUSED = 2;
const BUFFERING = 3;
const CUED = 5;
/**

@@ -70,9 +79,11 @@ * Attach an event listener to a YouTube player.

useEffect(() => {
if (handler) {
player === null || player === void 0 ? void 0 : player.addEventListener(event, handler);
if (handler && player) {
player.addEventListener(event, handler);
}
return () => {
if (handler) {
player === null || player === void 0 ? void 0 : player.removeEventListener(event, handler);
// If the iframe was already deleted, removing event
// listeners is unnecessary, and can actually cause a crash.
if (handler && player && player.getIframe()) {
player.removeEventListener(event, handler);
}

@@ -83,19 +94,13 @@ };

/**
* @param {React.RefObject<HTMLElement>} container
* @param {import('../index').YouTubeOptions} options
* @return {YT.PlayerVars}
*/
function useYouTube(container, _ref) {
function getPlayerVars(_ref) {
let {
video,
startSeconds,
endSeconds,
width,
height,
lang,
paused,
muted,
volume,
playbackRate,
muted = false,
autoplay = false,

@@ -110,2 +115,39 @@ showCaptions = false,

showRelatedVideos = true,
origin = typeof window.location === 'object' ? window.location.origin : undefined
} = _ref;
return {
autoplay: autoplay ? 1 : 0,
cc_load_policy: showCaptions ? 1 : 0,
controls: controls ? 1 : 0,
disablekb: disableKeyboard ? 1 : 0,
fs: allowFullscreen ? 1 : 0,
hl: lang,
iv_load_policy: annotations ? 1 : 3,
start: startSeconds,
end: endSeconds,
modestbranding: modestBranding ? 1 : 0,
playsinline: playsInline ? 1 : 0,
rel: showRelatedVideos ? 1 : 0,
mute: muted ? 1 : 0,
origin
};
}
/**
* @param {React.RefObject<HTMLElement>} container
* @param {import('../index').YouTubeOptions} options
*/
function useYouTube(container, options) {
const {
video,
startSeconds,
endSeconds,
width,
height,
paused,
muted,
volume,
playbackRate,
autoplay = false,
onReady,

@@ -121,5 +163,5 @@ onError,

onEnd = () => {}
} = _ref;
// Storing the player in the very first hook makes it easier to
} = options; // Storing the player in the very first hook makes it easier to
// find in React DevTools :)
const [player, setPlayer] = useState(

@@ -135,18 +177,2 @@ /** @type {YT.Player | null} */

if (!player) {
/** @type {YT.PlayerVars} */
const playerVars = {
autoplay: autoplay ? 1 : 0,
cc_load_policy: showCaptions ? 1 : 0,
controls: controls ? 1 : 0,
disablekb: disableKeyboard ? 1 : 0,
fs: allowFullscreen ? 1 : 0,
hl: lang,
iv_load_policy: annotations ? 1 : 3,
start: startSeconds,
end: endSeconds,
modestbranding: modestBranding ? 1 : 0,
playsinline: playsInline ? 1 : 0,
rel: showRelatedVideos ? 1 : 0
};
createPlayer.current = () => new YT.Player(container.current, {

@@ -156,3 +182,4 @@ videoId: video,

height,
playerVars,
host: options.host,
playerVars: getPlayerVars(options),
events: {

@@ -166,23 +193,44 @@ onReady: event => {

useLayoutEffect(() => {
/** @type {YT.Player|null} */
let instance = null;
let cancelled = false;
getSdk(() => {
if (!cancelled) {
instance = createPlayer.current();
}
});
return () => {
var _instance;
cancelled = true; // Destroying the player here means that some other hooks cannot access its methods anymore,
// so they do need to be careful in their unsubscribe effects.
// There isn't really a way around this aside from manually implementing parts of the
// `destroy()` method.
// It's tempting to just remove the iframe here just in time for React to move in,
// but we must use `.destroy()` to avoid memory leaks, since the YouTube SDK holds on
// to references to player objects globally.
(_instance = instance) === null || _instance === void 0 ? void 0 : _instance.destroy();
};
}, []);
const handlePlayerStateChange = useCallback(event => {
const State = YT.PlayerState;
switch (event.data) {
case State.CUED:
case CUED:
onCued(event);
break;
case State.BUFFERING:
case BUFFERING:
onBuffering(event);
break;
case State.PAUSED:
case PAUSED:
onPause(event);
break;
case State.PLAYING:
case PLAYING:
onPlaying(event);
break;
case State.ENDED:
case ENDED:
onEnd(event);

@@ -259,23 +307,3 @@ break;

}
}, [player, video]); // The effect that manages the player's lifetime.
// This must be done at the end to ensure that `.destroy()` runs last.
// Else, other hooks will attempt to do things like `.removeEventListener()`
// after the player is destroyed.
useEffect(() => {
/** @type {YT.Player|null} */
let instance = null;
let cancelled = false;
getSdk(() => {
if (!cancelled) {
instance = createPlayer.current();
}
});
return () => {
var _instance;
cancelled = true;
(_instance = instance) === null || _instance === void 0 ? void 0 : _instance.destroy();
};
}, []);
}, [player, video]);
return player;

@@ -308,3 +336,3 @@ }

/**
* An 11-character string representing a YouTube video ID..
* An 11-character string representing a YouTube video ID.
*/

@@ -324,3 +352,3 @@ video: PropTypes__default["default"].string,

/**
* Inline style for container element.
* Inline style for the player element.
*/

@@ -344,3 +372,4 @@ style: PropTypes__default["default"].object,

paused: PropTypes__default["default"].bool,
// eslint-disable-line react/no-unused-prop-types
host: PropTypes__default["default"].string,
origin: PropTypes__default["default"].string,
// Player parameters

@@ -347,0 +376,0 @@

@@ -19,2 +19,14 @@ /// <reference types="youtube" />

/**
* YouTube host to use: 'https://www.youtube.com' or 'https://www.youtube-nocookie.com'.
*
* @default 'https://www.youtube.com'
*/
host?: string;
/**
* The YouTube API will usually default this value correctly. It is exposed for completeness.
*/
origin?: string;
/**
* Pause the video.

@@ -30,2 +42,4 @@ */

* https://developers.google.com/youtube/player_parameters#autoplay
*
* @default false
*/

@@ -37,2 +51,4 @@ autoplay?: boolean;

* https://developers.google.com/youtube/player_parameters#cc_load_policy
*
* @default false
*/

@@ -44,2 +60,4 @@ showCaptions?: boolean;

* https://developers.google.com/youtube/player_parameters#controls
*
* @default true
*/

@@ -51,2 +69,4 @@ controls?: boolean;

* https://developers.google.com/youtube/player_parameters#disablekb
*
* @default false
*/

@@ -58,2 +78,4 @@ disableKeyboard?: boolean;

* https://developers.google.com/youtube/player_parameters#fs
*
* @default true
*/

@@ -72,2 +94,4 @@ allowFullscreen?: boolean;

* https://developers.google.com/youtube/player_parameters#iv_load_policy
*
* @default true
*/

@@ -91,2 +115,4 @@ annotations?: boolean;

* https://developers.google.com/youtube/player_parameters#modestbranding
*
* @default false
*/

@@ -98,2 +124,4 @@ modestBranding?: boolean;

* https://developers.google.com/youtube/player_parameters#playsinline
*
* @default false
*/

@@ -105,2 +133,4 @@ playsInline?: boolean;

* https://developers.google.com/youtube/player_parameters#rel
*
* @default true
*/

@@ -171,3 +201,3 @@ showRelatedVideos?: boolean;

/**
* Inline style for container element.
* Inline style for player element.
*/

@@ -178,2 +208,4 @@ style?: React.CSSProperties;

export function useYouTube(container: React.Ref<HTMLElement>, options: YouTubeOptions): YT.Player | null;
export default function YouTube(props: YouTubeProps): JSX.Element;
declare const YouTube: React.FunctionComponent<YouTubeProps>;
export default YouTube;
{
"name": "@u-wave/react-youtube",
"description": "YouTube player component for React.",
"version": "1.0.0-alpha.3",
"version": "1.0.0-alpha.4",
"author": "Renée Kooi <renee@kooi.me>",

@@ -31,3 +31,3 @@ "bugs": {

"min-react-env": "^1.0.1",
"mocha": "^9.0.0",
"mocha": "^10.0.0",
"prop-types-table": "^1.0.0",

@@ -34,0 +34,0 @@ "proxyquire": "^2.1.3",

# @u-wave/react-youtube
YouTube player component for React.
[Install][] - [Usage][] - [Demo][] - [Props][]
[Install][] - [Usage][] - [Demo][] - [Component API][] - [Hook API][]

@@ -23,11 +23,19 @@ ## Install

## Props
<a id="component"></a>
## `<YouTube />`
The `<YouTube />` component renders an iframe and attaches the YouTube player to it. It supports all the
same options as the `useYouTube` hook, plus a few to configure the iframe. If you need to do more with
the iframe than this component provides, consider using the `useYouTube` hook directly.
### Props
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| video | string | | An 11-character string representing a YouTube video ID.. |
| id | string | | DOM ID for the player element. |
| className | string | | CSS className for the player element. |
| style | object | | Inline style for container element. |
| video | string | | An 11-character string representing a YouTube video ID.. |
| width | number, string | | Width of the player element. |
| height | number, string | | Height of the player element. |
| host | string | https://www.youtube.com | YouTube host to use: 'https://www.youtube.com' or 'https://www.youtube-nocookie.com'. |
| origin | string | | The YouTube API will usually default this value correctly. It is exposed for completeness.<br>https://developers.google.com/youtube/player_parameters#origin |
| paused | bool | | Pause the video. |

@@ -51,7 +59,7 @@ | autoplay | bool | false | Whether the video should start playing automatically.<br>https://developers.google.com/youtube/player_parameters#autoplay |

| onError | function | | Sent when the player triggers an error. |
| onCued | function | () => {} | Sent when the video is cued and ready to play. |
| onBuffering | function | () => {} | Sent when the video is buffering. |
| onPlaying | function | () => {} | Sent when playback has been started or resumed. |
| onPause | function | () => {} | Sent when playback has been paused. |
| onEnd | function | () => {} | Sent when playback has stopped. |
| onCued | function | | Sent when the video is cued and ready to play. |
| onBuffering | function | | Sent when the video is buffering. |
| onPlaying | function | | Sent when playback has been started or resumed. |
| onPause | function | | Sent when playback has been paused. |
| onEnd | function | | Sent when playback has stopped. |
| onStateChange | function | | |

@@ -61,2 +69,57 @@ | onPlaybackRateChange | function | | |

<a id="hook"></a>
## `useYouTube(container, options)`
Create a YouTube player at `container`. `container` must be a ref object.
Returns the `YT.Player` object, or `null` until the player is ready.
```js
import { useYouTube } from '@u-wave/react-youtube';
function Player() {
const container = useRef(null);
const player = useYouTube(container, {
video: 'x2to0hs',
autoplay: true,
});
console.log(player?.getVideoUrl());
return <div ref={container} />;
}
```
### Options
| Name | Type | Default | Description |
|:-----|:-----|:-----|:-----|
| video | string | | An 11-character string representing a YouTube video ID.. |
| width | number, string | | Width of the player element. |
| height | number, string | | Height of the player element. |
| host | string | https://www.youtube.com | YouTube host to use: 'https://www.youtube.com' or 'https://www.youtube-nocookie.com'. |
| origin | string | | The YouTube API will usually default this value correctly. It is exposed for completeness.<br>https://developers.google.com/youtube/player_parameters#origin |
| paused | bool | | Pause the video. |
| autoplay | bool | false | Whether the video should start playing automatically.<br>https://developers.google.com/youtube/player_parameters#autoplay |
| showCaptions | bool | false | Whether to show captions below the video.<br>https://developers.google.com/youtube/player_parameters#cc_load_policy |
| controls | bool | true | Whether to show video controls.<br>https://developers.google.com/youtube/player_parameters#controls |
| disableKeyboard | bool | false | Ignore keyboard controls.<br>https://developers.google.com/youtube/player_parameters#disablekb |
| allowFullscreen | bool | true | Whether to display the fullscreen button.<br>https://developers.google.com/youtube/player_parameters#fs |
| lang | string | | The player's interface language. The parameter value is an ISO 639-1 two-letter language code or a fully specified locale.<br>https://developers.google.com/youtube/player_parameters#hl |
| annotations | bool | true | Whether to show annotations on top of the video.<br>https://developers.google.com/youtube/player_parameters#iv_load_policy |
| startSeconds | number | | Time in seconds at which to start playing the video.<br>https://developers.google.com/youtube/player_parameters#start |
| endSeconds | number | | Time in seconds at which to stop playing the video.<br>https://developers.google.com/youtube/player_parameters#end |
| modestBranding | bool | false | Remove most YouTube logos from the player.<br>https://developers.google.com/youtube/player_parameters#modestbranding |
| playsInline | bool | false | Whether to play the video inline on iOS, instead of fullscreen.<br>https://developers.google.com/youtube/player_parameters#playsinline |
| showRelatedVideos | bool | true | Whether to show related videos after the video is over.<br>https://developers.google.com/youtube/player_parameters#rel |
| volume | number | | The playback volume, **as a number between 0 and 1**. |
| muted | bool | | Whether the video's sound should be muted. |
| playbackRate | number | | Playback speed.<br>https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate |
| onReady | function | | Sent when the YouTube player API has loaded. |
| onError | function | | Sent when the player triggers an error. |
| onCued | function | | Sent when the video is cued and ready to play. |
| onBuffering | function | | Sent when the video is buffering. |
| onPlaying | function | | Sent when playback has been started or resumed. |
| onPause | function | | Sent when playback has been paused. |
| onEnd | function | | Sent when playback has stopped. |
| onStateChange | function | | |
| onPlaybackRateChange | function | | |
| onPlaybackQualityChange | function | | |
## Related

@@ -72,3 +135,4 @@ - [react-dailymotion][] - A Dailymotion component with a similar declarative API.

[Usage]: #usage
[Props]: #props
[Component API]: #component
[Hook API]: #hook
[Demo]: https://u-wave.net/react-youtube

@@ -75,0 +139,0 @@ [Demo source code]: ./example

// @ts-check
/* global YT */
/* global YT, window */
import React from 'react';

@@ -13,3 +13,11 @@ import PropTypes from 'prop-types';

} = React;
const useLayoutEffect = typeof document !== 'undefined' ? React.useLayoutEffect : useEffect;
// Player state numbers are documented to be constants, so we can inline them.
const ENDED = 0;
const PLAYING = 1;
const PAUSED = 2;
const BUFFERING = 3;
const CUED = 5;
/**

@@ -25,8 +33,10 @@ * Attach an event listener to a YouTube player.

useEffect(() => {
if (handler) {
player?.addEventListener(event, handler);
if (handler && player) {
player.addEventListener(event, handler);
}
return () => {
if (handler) {
player?.removeEventListener(event, handler);
// If the iframe was already deleted, removing event
// listeners is unnecessary, and can actually cause a crash.
if (handler && player && player.getIframe()) {
player.removeEventListener(event, handler);
}

@@ -38,16 +48,10 @@ };

/**
* @param {React.RefObject<HTMLElement>} container
* @param {import('../index').YouTubeOptions} options
* @return {YT.PlayerVars}
*/
function useYouTube(container, {
video,
function getPlayerVars({
startSeconds,
endSeconds,
width,
height,
lang,
paused,
muted,
volume,
playbackRate,
muted = false,
autoplay = false,

@@ -62,13 +66,50 @@ showCaptions = false,

showRelatedVideos = true,
onReady,
onError,
onStateChange,
onPlaybackQualityChange,
onPlaybackRateChange,
onCued = () => {},
onBuffering = () => {},
onPlaying = () => {},
onPause = () => {},
onEnd = () => {},
origin = typeof window.location === 'object' ? window.location.origin : undefined,
}) {
return {
autoplay: autoplay ? 1 : 0,
cc_load_policy: showCaptions ? 1 : 0,
controls: controls ? 1 : 0,
disablekb: disableKeyboard ? 1 : 0,
fs: allowFullscreen ? 1 : 0,
hl: lang,
iv_load_policy: annotations ? 1 : 3,
start: startSeconds,
end: endSeconds,
modestbranding: modestBranding ? 1 : 0,
playsinline: playsInline ? 1 : 0,
rel: showRelatedVideos ? 1 : 0,
mute: muted ? 1 : 0,
origin,
};
}
/**
* @param {React.RefObject<HTMLElement>} container
* @param {import('../index').YouTubeOptions} options
*/
function useYouTube(container, options) {
const {
video,
startSeconds,
endSeconds,
width,
height,
paused,
muted,
volume,
playbackRate,
autoplay = false,
onReady,
onError,
onStateChange,
onPlaybackQualityChange,
onPlaybackRateChange,
onCued = () => {},
onBuffering = () => {},
onPlaying = () => {},
onPause = () => {},
onEnd = () => {},
} = options;
// Storing the player in the very first hook makes it easier to

@@ -84,18 +125,2 @@ // find in React DevTools :)

if (!player) {
/** @type {YT.PlayerVars} */
const playerVars = {
autoplay: autoplay ? 1 : 0,
cc_load_policy: showCaptions ? 1 : 0,
controls: controls ? 1 : 0,
disablekb: disableKeyboard ? 1 : 0,
fs: allowFullscreen ? 1 : 0,
hl: lang,
iv_load_policy: annotations ? 1 : 3,
start: startSeconds,
end: endSeconds,
modestbranding: modestBranding ? 1 : 0,
playsinline: playsInline ? 1 : 0,
rel: showRelatedVideos ? 1 : 0,
};
createPlayer.current = () => new YT.Player(container.current, {

@@ -105,3 +130,4 @@ videoId: video,

height,
playerVars,
host: options.host,
playerVars: getPlayerVars(options),
events: {

@@ -115,18 +141,41 @@ onReady: (event) => {

useLayoutEffect(() => {
/** @type {YT.Player|null} */
let instance = null;
let cancelled = false;
loadSdk(() => {
if (!cancelled) {
instance = createPlayer.current();
}
});
return () => {
cancelled = true;
// Destroying the player here means that some other hooks cannot access its methods anymore,
// so they do need to be careful in their unsubscribe effects.
// There isn't really a way around this aside from manually implementing parts of the
// `destroy()` method.
// It's tempting to just remove the iframe here just in time for React to move in,
// but we must use `.destroy()` to avoid memory leaks, since the YouTube SDK holds on
// to references to player objects globally.
instance?.destroy();
};
}, []);
const handlePlayerStateChange = useCallback((event) => {
const State = YT.PlayerState;
switch (event.data) {
case State.CUED:
case CUED:
onCued(event);
break;
case State.BUFFERING:
case BUFFERING:
onBuffering(event);
break;
case State.PAUSED:
case PAUSED:
onPause(event);
break;
case State.PLAYING:
case PLAYING:
onPlaying(event);
break;
case State.ENDED:
case ENDED:
onEnd(event);

@@ -207,23 +256,2 @@ break;

// The effect that manages the player's lifetime.
// This must be done at the end to ensure that `.destroy()` runs last.
// Else, other hooks will attempt to do things like `.removeEventListener()`
// after the player is destroyed.
useEffect(() => {
/** @type {YT.Player|null} */
let instance = null;
let cancelled = false;
loadSdk(() => {
if (!cancelled) {
instance = createPlayer.current();
}
});
return () => {
cancelled = true;
instance?.destroy();
};
}, []);
return player;

@@ -256,3 +284,3 @@ }

/**
* An 11-character string representing a YouTube video ID..
* An 11-character string representing a YouTube video ID.
*/

@@ -269,3 +297,3 @@ video: PropTypes.string,

/**
* Inline style for container element.
* Inline style for the player element.
*/

@@ -291,4 +319,7 @@ style: PropTypes.object, // eslint-disable-line react/forbid-prop-types

*/
paused: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
paused: PropTypes.bool,
host: PropTypes.string,
origin: PropTypes.string,
// Player parameters

@@ -295,0 +326,0 @@

@@ -1,2 +0,2 @@

/* global window, document */
/* global YT, document */

@@ -11,3 +11,3 @@ function loadSdk() {

script.onload = null;
window.YT.ready(resolve);
YT.ready(resolve);
};

@@ -25,6 +25,7 @@ script.onerror = () => {

let sdk = null;
/** @param {(sdk: typeof YT) => void} callback */
export default function getSdk(callback) {
if (typeof window.YT === 'object' && typeof window.YT.ready === 'function') {
if (typeof YT === 'object' && typeof YT.ready === 'function') {
// A YouTube SDK is already loaded, so reuse that
window.YT.ready(callback);
YT.ready(callback);
return;

@@ -31,0 +32,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc