diff --git a/.changeset/shiny-signs-agree.md b/.changeset/shiny-signs-agree.md
new file mode 100644
index 0000000..900fad7
--- /dev/null
+++ b/.changeset/shiny-signs-agree.md
@@ -0,0 +1,8 @@
+---
+"react-native-youtube-bridge": patch
+"@react-native-youtube-bridge/react": patch
+"@react-native-youtube-bridge/core": patch
+"@react-native-youtube-bridge/web": patch
+---
+
+chore: reformat code with oxfmt
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 6e42fec..1588cfd 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -36,7 +36,7 @@ body:
attributes:
label: Library version
description: What version of the library are you using?
- placeholder: "x.x.x"
+ placeholder: 'x.x.x'
validations:
required: true
- type: textarea
@@ -61,7 +61,7 @@ body:
- type: input
id: reproducible-example
attributes:
- label: Reproducible example repository
+ label: Reproducible example repository
description: Please provide a link to a repository on GitHub with a reproducible example.
validations:
required: true
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index e587b8c..9a0be73 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -13,10 +13,10 @@ runs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
- version: 10.11.0
+ version: 10.29.3
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7fce704..be56192 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,13 +15,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup
uses: ./.github/actions/setup
- name: Lint files
- run: pnpm run lint
+ run: pnpm run lint:ci
+
+ - name: Format check
+ run: pnpm run format:check
- name: Build dependencies first
run: |
@@ -35,7 +38,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup
uses: ./.github/actions/setup
@@ -47,7 +50,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup
uses: ./.github/actions/setup
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4613fe1..e1b64f2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,10 +25,10 @@ jobs:
id: changesets
uses: changesets/action@v1
with:
- commit: "chore(release): version packages"
- title: "chore(release): version packages"
- version: "pnpm changeset version"
- publish: "pnpm changeset publish"
+ commit: 'chore(release): version packages'
+ title: 'chore(release): version packages'
+ version: 'pnpm changeset version'
+ publish: 'pnpm changeset publish'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 7b6c9db..35865a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,10 +5,6 @@
# XDE
.expo/
-# VSCode
-.vscode/
-jsconfig.json
-
# Xcode
#
build/
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
new file mode 100644
index 0000000..36e6dbe
--- /dev/null
+++ b/.oxfmtrc.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
+ "ignorePatterns": ["lib", "node_modules", "ios", "android", "dist", "**/*.md"],
+ "experimentalSortImports": {
+ "groups": [
+ ["side-effect"],
+ ["builtin"],
+ ["external", "type-external"],
+ ["internal", "type-internal"],
+ ["parent", "type-parent"],
+ ["sibling", "type-sibling"],
+ ["index", "type-index"]
+ ]
+ },
+ "experimentalSortPackageJson": {
+ "sortScripts": true
+ },
+ "singleQuote": true
+}
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..38453e4
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "plugins": ["react", "typescript", "eslint", "import", "unicorn"],
+ "categories": {
+ "correctness": "error",
+ "perf": "error",
+ "nursery": "error",
+ "suspicious": "error"
+ },
+ "rules": {
+ "react/react-in-jsx-scope": "off",
+ "import/no-unassigned-import": ["error", { "allow": ["**/*.css"] }],
+ "unicorn/require-post-message-target-origin": "off"
+ },
+ "settings": {
+ "jsx-a11y": {
+ "polymorphicPropName": null,
+ "components": {},
+ "attributes": {}
+ },
+ "react": {
+ "formComponents": [],
+ "linkComponents": [],
+ "version": null,
+ "componentWrapperFunctions": []
+ },
+ "jsdoc": {
+ "ignorePrivate": false,
+ "ignoreInternal": false,
+ "ignoreReplacesDocs": true,
+ "overrideReplacesDocs": true,
+ "augmentsExtendsReplacesDocs": false,
+ "implementsReplacesDocs": false,
+ "exemptDestructuredRootsFromChecks": false,
+ "tagNamePreference": {}
+ },
+ "vitest": {
+ "typecheck": false
+ }
+ },
+ "overrides": [
+ {
+ "files": ["packages/**/src/__tests__/*.test.tsx"],
+ "plugins": ["jest"],
+ "env": {
+ "jest": true
+ }
+ }
+ ],
+ "env": {
+ "builtin": true,
+ "node": true,
+ "es6": true,
+ "browser": true
+ },
+ "globals": {
+ "__DEV__": "readonly"
+ },
+ "ignorePatterns": ["lib", "node_modules", "ios", "android", "dist"]
+}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..99e2f7d
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["oxc.oxc-vscode"]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..2aae66a
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "editor.defaultFormatter": "oxc.oxc-vscode",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.oxc": "always"
+ }
+}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 45d257b..8b4fcfd 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,4 +1,3 @@
-
# Contributor Covenant Code of Conduct
## Our Pledge
@@ -18,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the overall
+- Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
-* The use of sexualized language or imagery, and sexual attention or advances of
+- The use of sexualized language or imagery, and sexual attention or advances of
any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email address,
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
+- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cb46d02..3e08753 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -88,9 +88,9 @@ Our pre-commit hooks verify that your commit message matches this format when co
### Linting and tests
-[biomejs](https://biomejs.dev/), [TypeScript](https://www.typescriptlang.org/)
+[oxlint](https://oxc.rs/docs/guide/usage/linter.html), [oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [TypeScript](https://www.typescriptlang.org/)
-We use [TypeScript](https://www.typescriptlang.org/) for type checking, [biomejs](https://biomejs.dev/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
+We use [TypeScript](https://www.typescriptlang.org/) for type checking, [oxlint](https://oxc.rs/docs/guide/usage/linter.html) for linting, [oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) for formatting the code, and [Jest](https://jestjs.io/) for testing.
Our pre-commit hooks verify that the linter and tests pass when committing.
diff --git a/README-ko_kr.md b/README-ko_kr.md
index e16e4ce..edecdf1 100644
--- a/README-ko_kr.md
+++ b/README-ko_kr.md
@@ -6,10 +6,11 @@
> **V1 사용자:** [V1 문서](/packages/react-native-youtube-bridge/docs/v1.md) | [V2 마이그레이션 가이드](/packages/react-native-youtube-bridge/docs/migration-v2.md)
## 개요
-React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이 필요합니다.
-하지만 현재 지속적으로 유지보수되고 있는 React Native용 YouTube 플레이어 라이브러리가 없는 상황입니다. (가장 인기 있는 react-native-youtube-iframe의 [최근 릴리즈는 2023년 07월 02일](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
-`react-native-youtube-bridge`는 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference)를 React Native에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다.
+React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이 필요합니다.
+하지만 현재 지속적으로 유지보수되고 있는 React Native용 YouTube 플레이어 라이브러리가 없는 상황입니다. (가장 인기 있는 react-native-youtube-iframe의 [최근 릴리즈는 2023년 07월 02일](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge`는 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference)를 React Native에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다.
- ✅ TypeScript 지원
- ✅ iOS, Android, Web 플랫폼 지원
@@ -22,6 +23,7 @@ React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이
- ✅ 유연한 렌더링 모드 (인라인 HTML & 웹뷰)
## 예제
+
> 빠른 시작을 원하신다면 [예제](/example/)를 확인해보세요.
- [웹 데모](https://react-native-youtube-bridge-example.pages.dev/)
@@ -49,20 +51,19 @@ bun add react-native-youtube-bridge
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
function App() {
- const videoIdOrUrl = 'AbZH7XWDW_k'
+ const videoIdOrUrl = 'AbZH7XWDW_k';
// OR useYouTubePlayer({ videoId: 'AbZH7XWDW_k' })
// OR useYouTubePlayer({ url: 'https://youtube.com/watch?v=AbZH7XWDW_k' })
const player = useYouTubePlayer(videoIdOrUrl);
- return (
-
- );
+ return ;
}
```
### 이벤트
-YouTube iframe API의 상태 변화를 애플리케이션에 전달하기 위해 [이벤트](https://developers.google.com/youtube/iframe_api_reference#Events)를 발생시킵니다.
+
+YouTube iframe API의 상태 변화를 애플리케이션에 전달하기 위해 [이벤트](https://developers.google.com/youtube/iframe_api_reference#Events)를 발생시킵니다.
`useYouTubeEvent` hook을 사용하여 완벽한 타입 추론을 지원하며, 두 가지 방법으로 이벤트를 쉽게 감지하여 사용할 수 있습니다.
@@ -89,20 +90,20 @@ function App() {
Alert.alert('Error', `Player error (${error.code}): ${error.message}`);
});
- return (
-
- );
+ return ;
}
```
`useYouTubeEvent` hook은 callback으로 값을 전달받는 방식과 state로 값을 바로 사용할 수 있는 두 가지 방법을 제공합니다.
+
1. Callback 방식: 의존성에 따라 리렌더링이 필요한 경우 4번째 인자에 dependency array를 주입해주면 됩니다.
2. State 방식:
1. `progress` event의 경우 3번째 인자에 interval 값을 설정할 수 있습니다. (기본값: 1000ms)
2. 나머지 event의 경우 3번째 인자에 기본 값을 설정할 수 있습니다.
### 기능
-YouTube iframe API의 [함수들](https://developers.google.com/youtube/iframe_api_reference#Functions)을 `useYouTubePlayer`를 통해 반환된 player 인스턴스 메서드를 호출하여 음소거, 재생, 볼륨 조절 등 다양한 플레이어 기능을 제어할 수 있습니다.
+
+YouTube iframe API의 [함수들](https://developers.google.com/youtube/iframe_api_reference#Functions)을 `useYouTubePlayer`를 통해 반환된 player 인스턴스 메서드를 호출하여 음소거, 재생, 볼륨 조절 등 다양한 플레이어 기능을 제어할 수 있습니다.
```tsx
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
@@ -156,11 +157,12 @@ function App() {
- )
+ );
}
```
### 초기 플레이어 매개변수
+
YouTube 내장 플레이어의 [매개변수](https://developers.google.com/youtube/player_parameters#Parameters)를 설정하여 초기 재생 환경을 맞춤화할 수 있습니다.
```tsx
@@ -175,13 +177,12 @@ function App() {
muted: true,
});
- return (
-
- );
+ return ;
}
```
### 스타일
+
YouTube 플레이어의 스타일을 원하는 대로 커스터마이징할 수 있습니다.
```tsx
@@ -199,19 +200,24 @@ function App() {
aspectRatio: 16 / 9,
}}
// iOS, Android 플랫폼 지원
- webViewStyle={{
- // ...
- }}
+ webViewStyle={
+ {
+ // ...
+ }
+ }
// iOS, Android 플랫폼 지원
- webViewProps={{
- // ...
- }}
+ webViewProps={
+ {
+ // ...
+ }
+ }
/>
- )
+ );
}
```
### 재생 진행률 추적
+
- `useYouTubeEvent` hook을 사용하여 `progress` 이벤트의 리스너를 등록하여 재생 진행률을 추적할 수 있습니다.
- 세 번째 인자에 interval 값을 설정하여 해당 간격(ms)마다 이벤트가 호출됩니다.
- interval을 원치 않으면 `0`으로 설정하면 됩니다.
@@ -224,15 +230,13 @@ function App() {
const player = useYouTubePlayer(videoIdOrUrl);
const progress = useYouTubeEvent(player, 'progress', progressInterval);
- return (
-
- )
+ return ;
}
```
### 플레이어 렌더링 및 소스 설정 (ios, android)
-**인라인 HTML vs 웹뷰 모드**
+**인라인 HTML vs 웹뷰 모드**
YouTube 플레이어 렌더링 방식을 제어하고 호환성을 위한 소스 URL을 설정합니다.
1. **인라인 HTML 모드** (`useInlineHtml: true`)는 앱 내에서 직접 HTML을 로드하여 플레이어를 렌더링합니다. (default)
@@ -242,6 +246,7 @@ YouTube 플레이어 렌더링 방식을 제어하고 호환성을 위한 소스
> [!NOTE]
> **webViewUrl 활용법**
+>
> - `useInlineHtml: true`인 경우: WebView source의 HTML `baseUrl`로 설정됩니다.
> - `useInlineHtml: false`인 경우: WebView source의 `uri`를 override합니다.
>
@@ -280,14 +285,17 @@ export default CustomPlayerPage;
> 자세한 내용은 [웹 플레이어 가이드](./packages/web/)를 참고해 주세요.
### YouTube oEmbed API
-`useYoutubeOEmbed` 훅을 통해 YouTube 비디오의 메타데이터를 가져올 수 있습니다.
+
+`useYoutubeOEmbed` 훅을 통해 YouTube 비디오의 메타데이터를 가져올 수 있습니다.
이 훅은 YouTube URL만 지원합니다.
```tsx
import { useYoutubeOEmbed } from 'react-native-youtube-bridge';
function App() {
- const { oEmbed, isLoading, error } = useYoutubeOEmbed('https://www.youtube.com/watch?v=AbZH7XWDW_k');
+ const { oEmbed, isLoading, error } = useYoutubeOEmbed(
+ 'https://www.youtube.com/watch?v=AbZH7XWDW_k',
+ );
if (isLoading) return Loading...;
if (error) return Error: {error.message};
@@ -296,12 +304,12 @@ function App() {
return (
<>
{oEmbed.title}
-
>
- )
+ );
}
```
diff --git a/README.md b/README.md
index 2186645..dde7684 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,11 @@
> **V1 users:** [V1 Documentation](/packages/react-native-youtube-bridge/docs/v1.md) | [V2 Migration Guide](/packages/react-native-youtube-bridge/docs/migration-v2.md)
## Overview
-Using a YouTube player in React Native requires complex setup and configuration.
-However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
-`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
+Using a YouTube player in React Native requires complex setup and configuration.
+However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
- ✅ TypeScript support
- ✅ iOS, Android, and Web platform support
@@ -50,21 +51,19 @@ bun add react-native-youtube-bridge
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
function App() {
- const videoIdOrUrl = 'AbZH7XWDW_k'
+ const videoIdOrUrl = 'AbZH7XWDW_k';
// OR useYouTubePlayer({ videoId: 'AbZH7XWDW_k' })
// OR useYouTubePlayer({ url: 'https://youtube.com/watch?v=AbZH7XWDW_k' })
const player = useYouTubePlayer(videoIdOrUrl);
- return (
-
- );
+ return ;
}
```
### Events
-[Events](https://developers.google.com/youtube/iframe_api_reference#Events) are fired to communicate YouTube iframe API state changes to your application.
+[Events](https://developers.google.com/youtube/iframe_api_reference#Events) are fired to communicate YouTube iframe API state changes to your application.
The `useYouTubeEvent` hook provides complete type inference and allows you to easily detect and use events in two ways.
@@ -91,9 +90,7 @@ function App() {
Alert.alert('Error', `Player error (${error.code}): ${error.message}`);
});
- return (
-
- );
+ return ;
}
```
@@ -106,7 +103,7 @@ The `useYouTubeEvent` hook provides two ways to receive values: callback-based a
### Features
-You can control various player features like muting, playing, and volume adjustment by calling methods on the player instance returned from `useYouTubePlayer`, which uses the YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions).
+You can control various player features like muting, playing, and volume adjustment by calling methods on the player instance returned from `useYouTubePlayer`, which uses the YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions).
```tsx
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
@@ -160,7 +157,7 @@ function App() {
- )
+ );
}
```
@@ -180,9 +177,7 @@ function App() {
muted: true,
});
- return (
-
- );
+ return ;
}
```
@@ -205,15 +200,19 @@ function App() {
aspectRatio: 16 / 9,
}}
// iOS, Android platform support
- webViewStyle={{
- // ...
- }}
+ webViewStyle={
+ {
+ // ...
+ }
+ }
// iOS, Android platform support
- webViewProps={{
- // ...
- }}
+ webViewProps={
+ {
+ // ...
+ }
+ }
/>
- )
+ );
}
```
@@ -231,24 +230,23 @@ function App() {
const player = useYouTubePlayer(videoIdOrUrl);
const progress = useYouTubeEvent(player, 'progress', progressInterval);
- return (
-
- )
+ return ;
}
```
### Player Rendering and Source Configuration (iOS, Android)
-**Inline HTML vs WebView Mode**
+**Inline HTML vs WebView Mode**
Control the YouTube player rendering method and set source URLs for compatibility.
1. **Inline HTML mode** (`useInlineHtml: true`) renders the player by loading HTML directly within the app. (default)
-2. **WebView mode** (`useInlineHtml: false`) loads an external player page.
+2. **WebView mode** (`useInlineHtml: false`) loads an external player page.
- The default URI is https://react-native-youtube-bridge.pages.dev.
- To use your own custom player page as an external WebView, build your player with `@react-native-youtube-bridge/web` and set the URL in the `webViewUrl` property. For detailed implementation instructions, please refer to the [Web Player Guide](https://github.com/react-native-bridges/react-native-youtube-bridge/tree/main/packages/web).
> [!NOTE]
> **webViewUrl Usage**
+>
> - When `useInlineHtml: true`: Set as the HTML `baseUrl` of the WebView source.
> - When `useInlineHtml: false`: Overrides the WebView source's `uri`.
>
@@ -288,14 +286,16 @@ export default CustomPlayerPage;
### YouTube oEmbed API
-You can fetch YouTube video metadata through the `useYoutubeOEmbed` hook.
+You can fetch YouTube video metadata through the `useYoutubeOEmbed` hook.
This hook only supports YouTube URLs.
```tsx
import { useYoutubeOEmbed } from 'react-native-youtube-bridge';
function App() {
- const { oEmbed, isLoading, error } = useYoutubeOEmbed('https://www.youtube.com/watch?v=AbZH7XWDW_k');
+ const { oEmbed, isLoading, error } = useYoutubeOEmbed(
+ 'https://www.youtube.com/watch?v=AbZH7XWDW_k',
+ );
if (isLoading) return Loading...;
if (error) return Error: {error.message};
@@ -304,12 +304,12 @@ function App() {
return (
<>
{oEmbed.title}
-
>
- )
+ );
}
```
diff --git a/biome.json b/biome.json
deleted file mode 100644
index 663aa84..0000000
--- a/biome.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
- "vcs": {
- "enabled": false,
- "clientKind": "git",
- "useIgnoreFile": false
- },
- "files": {
- "include": ["src/**/*", "example/src/**/*", "packages/**/*"],
- "ignoreUnknown": false,
- "ignore": ["lib", "node_modules", "ios", "android", "dist"]
- },
- "formatter": {
- "enabled": true,
- "indentStyle": "space"
- },
- "organizeImports": {
- "enabled": true
- },
- "linter": {
- "enabled": true,
- "rules": {
- "recommended": true,
- "suspicious": {
- "noExplicitAny": "warn"
- }
- }
- },
- "javascript": {
- "formatter": {
- "quoteStyle": "single",
- "lineWidth": 120
- }
- }
-}
diff --git a/example/package.json b/example/package.json
index af6f376..e5a2dec 100644
--- a/example/package.json
+++ b/example/package.json
@@ -1,11 +1,12 @@
{
"name": "example",
"version": "1.1.4",
+ "private": true,
"main": "index.js",
"scripts": {
- "start": "expo start --clear",
"android": "expo run:android",
"ios": "expo run:ios",
+ "start": "expo start --clear",
"web": "expo start --web --clear",
"web:build": "expo export --platform web --clear"
},
@@ -22,6 +23,5 @@
},
"devDependencies": {
"@babel/core": "^7.20.0"
- },
- "private": true
+ }
}
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 6821781..6f13588 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,5 +1,14 @@
import { useCallback, useEffect, useState } from 'react';
-import { Alert, Platform, SafeAreaView, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
+import {
+ Alert,
+ Platform,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
import {
PlayerState,
YoutubeView,
@@ -21,7 +30,9 @@ function App() {
const [isMuted, setIsMuted] = useState(false);
const [videoId, setVideoId] = useState('AbZH7XWDW_k');
const [progressInterval, setProgressInterval] = useState(1000);
- const { oEmbed, isLoading, error } = useYoutubeOEmbed(`https://www.youtube.com/watch?v=${videoId}`);
+ const { oEmbed, isLoading, error } = useYoutubeOEmbed(
+ `https://www.youtube.com/watch?v=${videoId}`,
+ );
const player = useYouTubePlayer(videoId, {
autoplay: true,
@@ -201,7 +212,10 @@ function App() {
setProgressInterval(progressInterval === 0 ? 1000 : 0)}
>
{progressInterval}ms interval
@@ -220,7 +234,10 @@ function App() {
{isPlaying ? '⏸️ Pause' : '▶️ Play'}
- player.stop()}>
+ player.stop()}
+ >
⏹️ Stop
@@ -235,7 +252,10 @@ function App() {
Volume control
-
+
{isMuted ? '🔇 Muted' : '🔊 Unmuted'}
diff --git a/lefthook.yml b/lefthook.yml
index 6e04ff6..d711908 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -2,13 +2,16 @@ pre-commit:
parallel: true
commands:
lint:
- glob: "*.{js,ts,jsx,tsx}"
- run: npx biome format --write {staged_files}
+ glob: '**/*.{js,ts,jsx,tsx}'
+ run: pnpm oxlint {staged_files}
+ format:
+ glob: '**/*.{js,ts,jsx,tsx}'
+ run: pnpm oxfmt {staged_files}
types:
- glob: "*.{js,ts,jsx,tsx}"
+ glob: '**/*.{js,ts,jsx,tsx}'
run: pnpm run typecheck
commit-msg:
parallel: true
commands:
commitlint:
- run: npx commitlint --edit
+ run: pnpm commitlint --edit
diff --git a/package.json b/package.json
index 87f5ddf..5a5f57c 100644
--- a/package.json
+++ b/package.json
@@ -1,38 +1,41 @@
{
"name": "@react-native-youtube-bridge/root",
- "description": "🎥 Easy-to-use YouTube player for React Native with cross-platform support",
"private": true,
- "scripts": {
- "example": "pnpm --filter example run",
- "test": "jest",
- "clear": "rimraf --glob **/dist",
- "typecheck": "pnpm -r run typecheck",
- "build": "pnpm -r run build",
- "lint": "biome format packages/** --write",
- "changeset": "changeset"
- },
+ "description": "🎥 Easy-to-use YouTube player for React Native with cross-platform support",
"keywords": [
- "react-native",
- "ios",
"android",
- "youtube",
+ "ios",
+ "react-native",
"react-native-youtube",
"react-native-youtube-bridge",
- "youtube-iframe-api",
- "react-native-youtube-iframe"
+ "react-native-youtube-iframe",
+ "youtube",
+ "youtube-iframe-api"
],
+ "homepage": "https://github.com/react-native-bridges/react-native-youtube-bridge#readme",
+ "bugs": {
+ "url": "https://github.com/react-native-bridges/react-native-youtube-bridge/issues"
+ },
+ "license": "MIT",
+ "author": "saseungmin (https://github.com/saseungmin)",
"repository": {
"type": "git",
"url": "git+https://github.com/react-native-bridges/react-native-youtube-bridge.git"
},
- "author": "saseungmin (https://github.com/saseungmin)",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/react-native-bridges/react-native-youtube-bridge/issues"
+ "scripts": {
+ "build": "pnpm -r run build",
+ "changeset": "changeset",
+ "clear": "rimraf --glob **/dist",
+ "example": "pnpm --filter example run",
+ "format": "oxfmt",
+ "format:check": "oxfmt --check",
+ "lint": "oxlint",
+ "lint:ci": "oxlint --format=github",
+ "lint:fix": "oxlint --fix",
+ "test": "jest",
+ "typecheck": "pnpm -r run typecheck"
},
- "homepage": "https://github.com/react-native-bridges/react-native-youtube-bridge#readme",
"devDependencies": {
- "@biomejs/biome": "1.9.4",
"@changesets/cli": "^2.29.4",
"@commitlint/config-conventional": "^19.6.0",
"@evilmartians/lefthook": "^1.5.0",
@@ -40,15 +43,20 @@
"@types/node": "^22.0.0",
"commitlint": "^19.6.1",
"jest": "^29.7.0",
+ "oxfmt": "^0.32.0",
+ "oxlint": "^1.47.0",
"typescript": "^5.8.3"
},
- "packageManager": "pnpm@10.11.0",
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"jest": {
- "modulePathIgnorePatterns": ["lib", "node_modules"]
- }
+ "modulePathIgnorePatterns": [
+ "lib",
+ "node_modules"
+ ]
+ },
+ "packageManager": "pnpm@10.29.3"
}
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index e719f69..7ddc8d3 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -79,7 +79,7 @@
```jsx
// Imperative, ref-based API
- const playerRef = useRef < PlayerControls > null;
+ const playerRef = useRef(null);
(https://github.com/saseungmin)",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/react-native-bridges/react-native-youtube-bridge.git"
+ },
+ "files": [
+ "dist",
+ "package.json"
+ ],
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
@@ -12,38 +36,14 @@
"require": "./dist/index.js"
}
},
- "files": [
- "dist",
- "package.json"
- ],
+ "publishConfig": {
+ "access": "public"
+ },
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch ./src",
"typecheck": "tsc --noEmit"
},
- "keywords": [
- "react-native",
- "ios",
- "android",
- "youtube",
- "react-native-youtube",
- "react-native-youtube-bridge",
- "youtube-iframe-api",
- "react-native-youtube-iframe"
- ],
- "repository": {
- "type": "git",
- "url": "git+https://github.com/react-native-bridges/react-native-youtube-bridge.git"
- },
- "author": "saseungmin (https://github.com/saseungmin)",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/react-native-bridges/react-native-youtube-bridge/issues"
- },
- "homepage": "https://github.com/react-native-bridges/react-native-youtube-bridge#readme",
- "publishConfig": {
- "access": "public"
- },
"devDependencies": {
"tsdown": "^0.12.8"
}
diff --git a/packages/core/src/types/iframe.ts b/packages/core/src/types/iframe.ts
index aa79091..3a0066c 100644
--- a/packages/core/src/types/iframe.ts
+++ b/packages/core/src/types/iframe.ts
@@ -1,6 +1,7 @@
-import type { PlaybackQuality, PlayerInfo, PlayerState } from '.';
import type { ERROR_CODES } from '../constants';
+import type { PlaybackQuality, PlayerInfo, PlayerState } from '.';
+
export interface IframeApiType {
Player: { new (elementId: string, options: Options): YouTubePlayer };
}
@@ -119,14 +120,22 @@ export interface YouTubePlayer {
endSeconds?: number | undefined;
suggestedQuality?: string | undefined;
}): Promise;
- cueVideoByUrl(mediaContentUrl: string, startSeconds?: number, suggestedQuality?: string): Promise;
+ cueVideoByUrl(
+ mediaContentUrl: string,
+ startSeconds?: number,
+ suggestedQuality?: string,
+ ): Promise;
cueVideoByUrl(video: {
mediaContentUrl: string;
startSeconds?: number | undefined;
endSeconds?: number | undefined;
suggestedQuality?: string | undefined;
}): Promise;
- loadVideoByUrl(mediaContentUrl: string, startSeconds?: number, suggestedQuality?: string): Promise;
+ loadVideoByUrl(
+ mediaContentUrl: string,
+ startSeconds?: number,
+ suggestedQuality?: string,
+ ): Promise;
loadVideoByUrl(video: {
mediaContentUrl: string;
startSeconds?: number | undefined;
diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts
index e04767f..94b6a62 100644
--- a/packages/core/src/types/index.ts
+++ b/packages/core/src/types/index.ts
@@ -12,7 +12,11 @@ type YoutubeErrorMessage = (typeof ERROR_CODES)[YoutubeErrorCode];
* const player = useYouTubePlayer({ url: 'https://www.youtube.com/watch?v=AbZH7XWDW_k' });
* ```
*/
-export type YoutubeSource = string | { videoId: string | undefined } | { url: string | undefined } | undefined;
+export type YoutubeSource =
+ | string
+ | { videoId: string | undefined }
+ | { url: string | undefined }
+ | undefined;
export type ProgressData = {
/**
diff --git a/packages/react-native-youtube-bridge/CHANGELOG.md b/packages/react-native-youtube-bridge/CHANGELOG.md
index c9fec47..c40a617 100644
--- a/packages/react-native-youtube-bridge/CHANGELOG.md
+++ b/packages/react-native-youtube-bridge/CHANGELOG.md
@@ -121,7 +121,7 @@
```jsx
// Imperative, ref-based API
- const playerRef = useRef < PlayerControls > null;
+ const playerRef = useRef(null);
**V1 사용자:** [V1 문서](/packages/react-native-youtube-bridge/docs/v1.md) | [V2 마이그레이션 가이드](/packages/react-native-youtube-bridge/docs/migration-v2.md)
## 개요
-React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이 필요합니다.
-하지만 현재 지속적으로 유지보수되고 있는 React Native용 YouTube 플레이어 라이브러리가 없는 상황입니다. (가장 인기 있는 react-native-youtube-iframe의 [최근 릴리즈는 2023년 07월 02일](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
-`react-native-youtube-bridge`는 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference)를 React Native에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다.
+React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이 필요합니다.
+하지만 현재 지속적으로 유지보수되고 있는 React Native용 YouTube 플레이어 라이브러리가 없는 상황입니다. (가장 인기 있는 react-native-youtube-iframe의 [최근 릴리즈는 2023년 07월 02일](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge`는 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference)를 React Native에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다.
- ✅ TypeScript 지원
- ✅ iOS, Android, Web 플랫폼 지원
@@ -22,6 +23,7 @@ React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이
- ✅ 유연한 렌더링 모드 (인라인 HTML & 웹뷰)
## 예제
+
> 빠른 시작을 원하신다면 [예제](/example/)를 확인해보세요.
- [웹 데모](https://react-native-youtube-bridge-example.pages.dev/)
@@ -49,20 +51,19 @@ bun add react-native-youtube-bridge
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
function App() {
- const videoIdOrUrl = 'AbZH7XWDW_k'
+ const videoIdOrUrl = 'AbZH7XWDW_k';
// OR useYouTubePlayer({ videoId: 'AbZH7XWDW_k' })
// OR useYouTubePlayer({ url: 'https://youtube.com/watch?v=AbZH7XWDW_k' })
const player = useYouTubePlayer(videoIdOrUrl);
- return (
-
- );
+ return ;
}
```
### 이벤트
-YouTube iframe API의 상태 변화를 애플리케이션에 전달하기 위해 [이벤트](https://developers.google.com/youtube/iframe_api_reference#Events)를 발생시킵니다.
+
+YouTube iframe API의 상태 변화를 애플리케이션에 전달하기 위해 [이벤트](https://developers.google.com/youtube/iframe_api_reference#Events)를 발생시킵니다.
`useYouTubeEvent` hook을 사용하여 완벽한 타입 추론을 지원하며, 두 가지 방법으로 이벤트를 쉽게 감지하여 사용할 수 있습니다.
@@ -89,20 +90,20 @@ function App() {
Alert.alert('Error', `Player error (${error.code}): ${error.message}`);
});
- return (
-
- );
+ return ;
}
```
`useYouTubeEvent` hook은 callback으로 값을 전달받는 방식과 state로 값을 바로 사용할 수 있는 두 가지 방법을 제공합니다.
+
1. Callback 방식: 의존성에 따라 리렌더링이 필요한 경우 4번째 인자에 dependency array를 주입해주면 됩니다.
2. State 방식:
1. `progress` event의 경우 3번째 인자에 interval 값을 설정할 수 있습니다. (기본값: 1000ms)
2. 나머지 event의 경우 3번째 인자에 기본 값을 설정할 수 있습니다.
### 기능
-YouTube iframe API의 [함수들](https://developers.google.com/youtube/iframe_api_reference#Functions)을 `useYouTubePlayer`를 통해 반환된 player 인스턴스 메서드를 호출하여 음소거, 재생, 볼륨 조절 등 다양한 플레이어 기능을 제어할 수 있습니다.
+
+YouTube iframe API의 [함수들](https://developers.google.com/youtube/iframe_api_reference#Functions)을 `useYouTubePlayer`를 통해 반환된 player 인스턴스 메서드를 호출하여 음소거, 재생, 볼륨 조절 등 다양한 플레이어 기능을 제어할 수 있습니다.
```tsx
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
@@ -156,11 +157,12 @@ function App() {
- )
+ );
}
```
### 초기 플레이어 매개변수
+
YouTube 내장 플레이어의 [매개변수](https://developers.google.com/youtube/player_parameters#Parameters)를 설정하여 초기 재생 환경을 맞춤화할 수 있습니다.
```tsx
@@ -175,13 +177,12 @@ function App() {
muted: true,
});
- return (
-
- );
+ return ;
}
```
### 스타일
+
YouTube 플레이어의 스타일을 원하는 대로 커스터마이징할 수 있습니다.
```tsx
@@ -199,19 +200,24 @@ function App() {
aspectRatio: 16 / 9,
}}
// iOS, Android 플랫폼 지원
- webViewStyle={{
- // ...
- }}
+ webViewStyle={
+ {
+ // ...
+ }
+ }
// iOS, Android 플랫폼 지원
- webViewProps={{
- // ...
- }}
+ webViewProps={
+ {
+ // ...
+ }
+ }
/>
- )
+ );
}
```
### 재생 진행률 추적
+
- `useYouTubeEvent` hook을 사용하여 `progress` 이벤트의 리스너를 등록하여 재생 진행률을 추적할 수 있습니다.
- 세 번째 인자에 interval 값을 설정하여 해당 간격(ms)마다 이벤트가 호출됩니다.
- interval을 원치 않으면 `0`으로 설정하면 됩니다.
@@ -224,15 +230,13 @@ function App() {
const player = useYouTubePlayer(videoIdOrUrl);
const progress = useYouTubeEvent(player, 'progress', progressInterval);
- return (
-
- )
+ return ;
}
```
### 플레이어 렌더링 및 소스 설정 (ios, android)
-**인라인 HTML vs 웹뷰 모드**
+**인라인 HTML vs 웹뷰 모드**
YouTube 플레이어 렌더링 방식을 제어하고 호환성을 위한 소스 URL을 설정합니다.
1. **인라인 HTML 모드** (`useInlineHtml: true`)는 앱 내에서 직접 HTML을 로드하여 플레이어를 렌더링합니다. (default)
@@ -242,6 +246,7 @@ YouTube 플레이어 렌더링 방식을 제어하고 호환성을 위한 소스
> [!NOTE]
> **webViewUrl 활용법**
+>
> - `useInlineHtml: true`인 경우: WebView source의 HTML `baseUrl`로 설정됩니다.
> - `useInlineHtml: false`인 경우: WebView source의 `uri`를 override합니다.
>
@@ -280,14 +285,17 @@ export default CustomPlayerPage;
> 자세한 내용은 [웹 플레이어 가이드](https://github.com/react-native-bridges/react-native-youtube-bridge/tree/main/packages/web)를 참고해 주세요.
### YouTube oEmbed API
-`useYoutubeOEmbed` 훅을 통해 YouTube 비디오의 메타데이터를 가져올 수 있습니다.
+
+`useYoutubeOEmbed` 훅을 통해 YouTube 비디오의 메타데이터를 가져올 수 있습니다.
이 훅은 YouTube URL만 지원합니다.
```tsx
import { useYoutubeOEmbed } from 'react-native-youtube-bridge';
function App() {
- const { oEmbed, isLoading, error } = useYoutubeOEmbed('https://www.youtube.com/watch?v=AbZH7XWDW_k');
+ const { oEmbed, isLoading, error } = useYoutubeOEmbed(
+ 'https://www.youtube.com/watch?v=AbZH7XWDW_k',
+ );
if (isLoading) return Loading...;
if (error) return Error: {error.message};
@@ -296,12 +304,12 @@ function App() {
return (
<>
{oEmbed.title}
-
>
- )
+ );
}
```
diff --git a/packages/react-native-youtube-bridge/README.md b/packages/react-native-youtube-bridge/README.md
index 652214a..b15ddc7 100644
--- a/packages/react-native-youtube-bridge/README.md
+++ b/packages/react-native-youtube-bridge/README.md
@@ -6,10 +6,11 @@
> **V1 users:** [V1 Documentation](/packages/react-native-youtube-bridge/docs/v1.md) | [V2 Migration Guide](/packages/react-native-youtube-bridge/docs/migration-v2.md)
## Overview
-Using a YouTube player in React Native requires complex setup and configuration.
-However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
-`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
+Using a YouTube player in React Native requires complex setup and configuration.
+However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
- ✅ TypeScript support
- ✅ iOS, Android, and Web platform support
@@ -50,21 +51,19 @@ bun add react-native-youtube-bridge
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
function App() {
- const videoIdOrUrl = 'AbZH7XWDW_k'
+ const videoIdOrUrl = 'AbZH7XWDW_k';
// OR useYouTubePlayer({ videoId: 'AbZH7XWDW_k' })
// OR useYouTubePlayer({ url: 'https://youtube.com/watch?v=AbZH7XWDW_k' })
const player = useYouTubePlayer(videoIdOrUrl);
- return (
-
- );
+ return ;
}
```
### Events
-[Events](https://developers.google.com/youtube/iframe_api_reference#Events) are fired to communicate YouTube iframe API state changes to your application.
+[Events](https://developers.google.com/youtube/iframe_api_reference#Events) are fired to communicate YouTube iframe API state changes to your application.
The `useYouTubeEvent` hook provides complete type inference and allows you to easily detect and use events in two ways.
@@ -91,9 +90,7 @@ function App() {
Alert.alert('Error', `Player error (${error.code}): ${error.message}`);
});
- return (
-
- );
+ return ;
}
```
@@ -106,7 +103,7 @@ The `useYouTubeEvent` hook provides two ways to receive values: callback-based a
### Features
-You can control various player features like muting, playing, and volume adjustment by calling methods on the player instance returned from `useYouTubePlayer`, which uses the YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions).
+You can control various player features like muting, playing, and volume adjustment by calling methods on the player instance returned from `useYouTubePlayer`, which uses the YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions).
```tsx
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
@@ -160,7 +157,7 @@ function App() {
- )
+ );
}
```
@@ -180,9 +177,7 @@ function App() {
muted: true,
});
- return (
-
- );
+ return ;
}
```
@@ -205,15 +200,19 @@ function App() {
aspectRatio: 16 / 9,
}}
// iOS, Android platform support
- webViewStyle={{
- // ...
- }}
+ webViewStyle={
+ {
+ // ...
+ }
+ }
// iOS, Android platform support
- webViewProps={{
- // ...
- }}
+ webViewProps={
+ {
+ // ...
+ }
+ }
/>
- )
+ );
}
```
@@ -231,24 +230,23 @@ function App() {
const player = useYouTubePlayer(videoIdOrUrl);
const progress = useYouTubeEvent(player, 'progress', progressInterval);
- return (
-
- )
+ return ;
}
```
### Player Rendering and Source Configuration (iOS, Android)
-**Inline HTML vs WebView Mode**
+**Inline HTML vs WebView Mode**
Control the YouTube player rendering method and set source URLs for compatibility.
1. **Inline HTML mode** (`useInlineHtml: true`) renders the player by loading HTML directly within the app. (default)
-2. **WebView mode** (`useInlineHtml: false`) loads an external player page.
+2. **WebView mode** (`useInlineHtml: false`) loads an external player page.
- The default URI is https://react-native-youtube-bridge.pages.dev.
- To use your own custom player page as an external WebView, build your player with `@react-native-youtube-bridge/web` and set the URL in the `webViewUrl` property. For detailed implementation instructions, please refer to the [Web Player Guide](https://github.com/react-native-bridges/react-native-youtube-bridge/tree/main/packages/web).
> [!NOTE]
> **webViewUrl Usage**
+>
> - When `useInlineHtml: true`: Set as the HTML `baseUrl` of the WebView source.
> - When `useInlineHtml: false`: Overrides the WebView source's `uri`.
>
@@ -288,14 +286,16 @@ export default CustomPlayerPage;
### YouTube oEmbed API
-You can fetch YouTube video metadata through the `useYoutubeOEmbed` hook.
+You can fetch YouTube video metadata through the `useYoutubeOEmbed` hook.
This hook only supports YouTube URLs.
```tsx
import { useYoutubeOEmbed } from 'react-native-youtube-bridge';
function App() {
- const { oEmbed, isLoading, error } = useYoutubeOEmbed('https://www.youtube.com/watch?v=AbZH7XWDW_k');
+ const { oEmbed, isLoading, error } = useYoutubeOEmbed(
+ 'https://www.youtube.com/watch?v=AbZH7XWDW_k',
+ );
if (isLoading) return Loading...;
if (error) return Error: {error.message};
@@ -304,12 +304,12 @@ function App() {
return (
<>
{oEmbed.title}
-
>
- )
+ );
}
```
diff --git a/packages/react-native-youtube-bridge/docs/migration-v2.md b/packages/react-native-youtube-bridge/docs/migration-v2.md
index e69453a..f8b88dd 100644
--- a/packages/react-native-youtube-bridge/docs/migration-v2.md
+++ b/packages/react-native-youtube-bridge/docs/migration-v2.md
@@ -4,20 +4,21 @@ This guide will help you migrate from react-native-youtube-bridge v1 to v2. The
## Overview of Changes
-| Aspect | v1 (Old) | v2 (New) |
-|--------|----------|----------|
-| **API Style** | Imperative (ref-based) | Declarative (hooks-based) |
-| **Component** | `YoutubePlayer` | `YoutubeView` + `useYouTubePlayer` |
-| **Event Handling** | Manual listeners + props | `useYouTubeEvent` hook |
-| **State Management** | Manual `useState` | Automatic reactive updates |
-| **Player Control** | `playerRef.current.method()` | `player.method()` |
-| **Configuration** | Component props | Hook parameters |
+| Aspect | v1 (Old) | v2 (New) |
+| -------------------- | ---------------------------- | ---------------------------------- |
+| **API Style** | Imperative (ref-based) | Declarative (hooks-based) |
+| **Component** | `YoutubePlayer` | `YoutubeView` + `useYouTubePlayer` |
+| **Event Handling** | Manual listeners + props | `useYouTubeEvent` hook |
+| **State Management** | Manual `useState` | Automatic reactive updates |
+| **Player Control** | `playerRef.current.method()` | `player.method()` |
+| **Configuration** | Component props | Hook parameters |
## Step-by-Step Migration
### 1. Component Replacement
**Before (v1):**
+
```jsx
import { YoutubePlayer } from 'react-native-youtube-bridge';
@@ -36,10 +37,11 @@ import { YoutubePlayer } from 'react-native-youtube-bridge';
onStateChange={handleStateChange}
onProgress={handleProgress}
onError={handleError}
-/>
+/>;
```
**After (v2):**
+
```jsx
import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';
@@ -51,10 +53,7 @@ const player = useYouTubePlayer(videoId, {
muted: true,
});
-
+;
```
### 2. Event Handling Migration
@@ -62,6 +61,7 @@ const player = useYouTubePlayer(videoId, {
The `useYouTubeEvent` hook provides complete type inference and allows you to handle events in two ways: **callback-based** and **state-based**.
**Before (v1):**
+
```jsx
// Manual event handlers and state management
const [isPlaying, setIsPlaying] = useState(false);
@@ -92,6 +92,7 @@ const handlePlaybackRateChange = useCallback((rate) => {
```
**After (v2):**
+
```jsx
// State-based event handling (reactive values)
const playbackRate = useYouTubeEvent(player, 'playbackRateChange', 1);
@@ -125,31 +126,41 @@ useYouTubeEvent(player, 'error', (error) => {
#### `useYouTubeEvent` Usage Patterns:
1. **State method** - Returns reactive values:
+
```jsx
// For progress events with custom interval
const progress = useYouTubeEvent(player, 'progress', 1000); // 1000ms interval
-
+
// For other events with default value
const playbackRate = useYouTubeEvent(player, 'playbackRateChange', 1);
const state = useYouTubeEvent(player, 'stateChange');
```
2. **Callback method** - For side effects and actions:
+
```jsx
// Simple callback
useYouTubeEvent(player, 'ready', (playerInfo) => {
console.log('Player ready:', playerInfo);
});
-
+
// With dependency array for re-rendering control
- useYouTubeEvent(player, 'stateChange', (state) => {
- // Handle state change
- }, [/* dependencies */]);
+ useYouTubeEvent(
+ player,
+ 'stateChange',
+ (state) => {
+ // Handle state change
+ },
+ [
+ /* dependencies */
+ ],
+ );
```
### 3. Player Control Migration
**Before (v1):**
+
```jsx
const playerRef = useRef(null);
@@ -171,6 +182,7 @@ const getPlayerInfo = async () => {
```
**After (v2):**
+
```jsx
const player = useYouTubePlayer(videoId, config);
@@ -228,4 +240,5 @@ The v2 API is designed to be more intuitive and reduce boilerplate code while pr
## Previous Versions
### [1.x.x] - Legacy Version
+
See [v1 documentation](./v1.md) for the previous imperative API.
diff --git a/packages/react-native-youtube-bridge/docs/v1.md b/packages/react-native-youtube-bridge/docs/v1.md
index 89b224d..b562988 100644
--- a/packages/react-native-youtube-bridge/docs/v1.md
+++ b/packages/react-native-youtube-bridge/docs/v1.md
@@ -1,10 +1,11 @@
# React Native Youtube Bridge (Version 1)
## Overview
-Using a YouTube player in React Native requires complex setup and configuration.
-However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
-`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
+Using a YouTube player in React Native requires complex setup and configuration.
+However, there are currently no actively maintained YouTube player libraries for React Native. (The most popular react-native-youtube-iframe's [latest release was July 2, 2023](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0))
+
+`react-native-youtube-bridge` is a library that makes it easy to use the [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) in React Native applications.
- ✅ TypeScript support
- ✅ iOS, Android, and Web platform support
@@ -16,6 +17,7 @@ However, there are currently no actively maintained YouTube player libraries for
- ✅ Flexible rendering modes (Inline HTML & WebView)
## Example
+
> For a quick start, check out the [example](/example/).
- [Web Demo](https://react-native-youtube-bridge-example.pages.dev/)
@@ -44,17 +46,18 @@ import { YoutubePlayer } from 'react-native-youtube-bridge';
function App() {
return (
-
- )
+ );
}
```
### Events
-The library fires [events](https://developers.google.com/youtube/iframe_api_reference#Events) to notify your application of YouTube iframe API state changes. You can subscribe to these events using callback functions.
+
+The library fires [events](https://developers.google.com/youtube/iframe_api_reference#Events) to notify your application of YouTube iframe API state changes. You can subscribe to these events using callback functions.
> 🔔 Note - Wrap callback functions with `useCallback` for performance optimization and to prevent abnormal behavior.
@@ -95,12 +98,13 @@ function App() {
onPlaybackQualityChange={handlePlaybackQualityChange}
onAutoplayBlocked={handleAutoplayBlocked}
/>
- )
+ );
}
```
### Functions
-You can control various player features like mute, play, volume, and more by calling YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions) through the `ref`.
+
+You can control various player features like mute, play, volume, and more by calling YouTube iframe API [functions](https://developers.google.com/youtube/iframe_api_reference#Functions) through the `ref`.
```tsx
function App() {
@@ -126,10 +130,7 @@ function App() {
return (
-
+
- )
+ );
}
```
### Player Parameters
+
You can customize the playback environment by configuring YouTube embedded player [parameters](https://developers.google.com/youtube/player_parameters#Parameters).
```tsx
@@ -175,11 +177,12 @@ function App() {
muted: true,
}}
/>
- )
+ );
}
```
### Styles
+
You can customize the YouTube player's styling to match your application's design.
```tsx
@@ -197,19 +200,24 @@ function App() {
aspectRatio: 16 / 9,
}}
// iOS, Android platform support
- webViewStyle={{
- // ...
- }}
+ webViewStyle={
+ {
+ // ...
+ }
+ }
// iOS, Android platform support
- webViewProps={{
- // ...
- }}
+ webViewProps={
+ {
+ // ...
+ }
+ }
/>
- )
+ );
}
```
### Playback Progress Tracking
+
- If `progressInterval` is provided, the `onProgress` callback will be invoked at the specified interval (in milliseconds).
- If `progressInterval` is `undefined`, `0`, or `null`, progress tracking is disabled and `onProgress` will not be called.
@@ -221,19 +229,13 @@ function App() {
setLoadedFraction(progress.loadedFraction);
}, []);
- return (
-
- )
+ return ;
}
```
### Player Rendering & Source Configuration (ios, android)
-**Inline HTML vs WebView Mode**
+**Inline HTML vs WebView Mode**
Control YouTube player rendering method and configure source URLs for compatibility.
1. **Inline HTML Mode** (`useInlineHtml: true`) renders the player by loading HTML directly within the app. (default)
@@ -243,6 +245,7 @@ Control YouTube player rendering method and configure source URLs for compatibil
> [!NOTE]
> **webViewUrl Usage**
+>
> - When `useInlineHtml: true`: Set as the `baseUrl` for WebView source HTML.
> - When `useInlineHtml: false`: Overrides the WebView source `uri`.
>
@@ -281,6 +284,7 @@ export default CustomPlayerPage;
> For more details, please refer to the [Web Player Guide](https://github.com/react-native-bridges/react-native-youtube-bridge/tree/main/packages/web).
### YouTube oEmbed API
+
Use the `useYoutubeOEmbed` hook to fetch YouTube video metadata.
This hook only supports YouTube URLs.
@@ -288,21 +292,23 @@ This hook only supports YouTube URLs.
import { useYoutubeOEmbed } from 'react-native-youtube-bridge';
function App() {
- const { oEmbed, isLoading, error } = useYoutubeOEmbed('https://www.youtube.com/watch?v=AbZH7XWDW_k');
-
- if (isLoading) return Loading...;
- if (error) return Error: {error.message};
- if (!oEmbed) return null;
-
- return (
- <>
- {oEmbed.title}
-
- >
- )
+ const { oEmbed, isLoading, error } = useYoutubeOEmbed(
+ 'https://www.youtube.com/watch?v=AbZH7XWDW_k',
+ );
+
+ if (isLoading) return Loading...;
+ if (error) return Error: {error.message};
+ if (!oEmbed) return null;
+
+ return (
+ <>
+ {oEmbed.title}
+
+ >
+ );
}
```
diff --git a/packages/react-native-youtube-bridge/package.json b/packages/react-native-youtube-bridge/package.json
index fa2b397..a3b1fc4 100644
--- a/packages/react-native-youtube-bridge/package.json
+++ b/packages/react-native-youtube-bridge/package.json
@@ -2,15 +2,25 @@
"name": "react-native-youtube-bridge",
"version": "2.1.4",
"description": "🎥 Easy-to-use YouTube player for React Native with cross-platform support",
- "main": "./lib/module/index.js",
- "types": "./lib/typescript/index.d.ts",
- "exports": {
- ".": {
- "source": "./src/index.tsx",
- "types": "./lib/typescript/index.d.ts",
- "default": "./lib/module/index.js"
- },
- "./package.json": "./package.json"
+ "keywords": [
+ "android",
+ "ios",
+ "react-native",
+ "react-native-youtube",
+ "react-native-youtube-bridge",
+ "react-native-youtube-iframe",
+ "youtube",
+ "youtube-iframe-api"
+ ],
+ "homepage": "https://github.com/react-native-bridges/react-native-youtube-bridge#readme",
+ "bugs": {
+ "url": "https://github.com/react-native-bridges/react-native-youtube-bridge/issues"
+ },
+ "license": "MIT",
+ "author": "saseungmin (https://github.com/saseungmin)",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/react-native-bridges/react-native-youtube-bridge.git"
},
"files": [
"lib",
@@ -31,35 +41,25 @@
"!**/__mocks__",
"!**/.*"
],
- "scripts": {
- "test": "jest",
- "typecheck": "tsc --noEmit",
- "clean": "del-cli lib",
- "build": "bob build"
- },
- "keywords": [
- "react-native",
- "ios",
- "android",
- "youtube",
- "react-native-youtube",
- "react-native-youtube-bridge",
- "youtube-iframe-api",
- "react-native-youtube-iframe"
- ],
- "repository": {
- "type": "git",
- "url": "git+https://github.com/react-native-bridges/react-native-youtube-bridge.git"
- },
- "author": "saseungmin (https://github.com/saseungmin)",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/react-native-bridges/react-native-youtube-bridge/issues"
+ "main": "./lib/module/index.js",
+ "types": "./lib/typescript/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./lib/typescript/index.d.ts",
+ "source": "./src/index.tsx",
+ "default": "./lib/module/index.js"
+ },
+ "./package.json": "./package.json"
},
- "homepage": "https://github.com/react-native-bridges/react-native-youtube-bridge#readme",
"publishConfig": {
"access": "public"
},
+ "scripts": {
+ "build": "bob build",
+ "clean": "del-cli lib",
+ "test": "jest",
+ "typecheck": "tsc --noEmit"
+ },
"dependencies": {
"@react-native-youtube-bridge/core": "workspace:*",
"@react-native-youtube-bridge/react": "workspace:*"
@@ -80,10 +80,15 @@
"react-native-webview": ">=11.0.0"
},
"jest": {
- "preset": "react-native",
"modulePathIgnorePatterns": [
"/lib/"
- ]
+ ],
+ "preset": "react-native"
+ },
+ "create-react-native-library": {
+ "languages": "js",
+ "type": "library",
+ "version": "0.50.3"
},
"react-native-builder-bob": {
"source": "src",
@@ -102,10 +107,5 @@
}
]
]
- },
- "create-react-native-library": {
- "languages": "js",
- "type": "library",
- "version": "0.50.3"
}
}
diff --git a/packages/react-native-youtube-bridge/src/YoutubeView.tsx b/packages/react-native-youtube-bridge/src/YoutubeView.tsx
index 0dd473f..baa0d98 100644
--- a/packages/react-native-youtube-bridge/src/YoutubeView.tsx
+++ b/packages/react-native-youtube-bridge/src/YoutubeView.tsx
@@ -1,15 +1,18 @@
+import { type MessageData, MATCH_URL_YOUTUBE } from '@react-native-youtube-bridge/core';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { type DataDetectorTypes, Dimensions, StyleSheet, Linking } from 'react-native';
-import WebView, { type WebViewMessageEvent } from 'react-native-webview';
+import { WebView, type WebViewMessageEvent } from 'react-native-webview';
import type { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes';
-import { type MessageData, MATCH_URL_YOUTUBE } from '@react-native-youtube-bridge/core';
-import YoutubeViewWrapper from './YoutubeViewWrapper';
import useCreateLocalPlayerHtml from './hooks/useCreateLocalPlayerHtml';
+import WebviewYoutubePlayerController from './modules/WebviewYoutubePlayerController';
+import {
+ INTERNAL_SET_CONTROLLER_INSTANCE,
+ INTERNAL_UPDATE_PROGRESS_INTERVAL,
+} from './modules/YoutubePlayer';
import type { YoutubeViewProps } from './types/youtube';
import { getYoutubeWebViewUrl } from './utils/youtube';
-import WebviewYoutubePlayerController from './modules/WebviewYoutubePlayerController';
-import { INTERNAL_SET_CONTROLLER_INSTANCE, INTERNAL_UPDATE_PROGRESS_INTERVAL } from './modules/YoutubePlayer';
+import YoutubeViewWrapper from './YoutubeViewWrapper';
const { width: screenWidth } = Dimensions.get('window');
@@ -37,7 +40,6 @@ function YoutubeView({
const createPlayerHTML = useCreateLocalPlayerHtml({ videoId, useInlineHtml, ...playerVars });
const webViewUrl = getYoutubeWebViewUrl(videoId, useInlineHtml, playerVars, webViewBaseUrl);
- // biome-ignore lint/correctness/useExhaustiveDependencies: webViewProps.source is intentionally excluded to prevent unnecessary re-renders
const webViewSource = useMemo(() => {
if (useInlineHtml) {
const webViewBaseUrlWithSlash =
@@ -50,10 +52,11 @@ function YoutubeView({
}
if (webViewUrl) {
- return { ...(webViewProps?.source ?? {}), uri: webViewUrl };
+ return { ...webViewProps?.source, uri: webViewUrl };
}
return undefined;
+ // oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, [useInlineHtml, createPlayerHTML, webViewBaseUrl, webViewUrl]);
const handleMessage = useCallback(
diff --git a/packages/react-native-youtube-bridge/src/YoutubeView.web.tsx b/packages/react-native-youtube-bridge/src/YoutubeView.web.tsx
index 70d7a81..23cdc13 100644
--- a/packages/react-native-youtube-bridge/src/YoutubeView.web.tsx
+++ b/packages/react-native-youtube-bridge/src/YoutubeView.web.tsx
@@ -1,10 +1,13 @@
+import { WebYoutubePlayerController } from '@react-native-youtube-bridge/core';
import { useEffect, useRef, useState } from 'react';
import { useWindowDimensions } from 'react-native';
-import { WebYoutubePlayerController } from '@react-native-youtube-bridge/core';
-import { INTERNAL_SET_CONTROLLER_INSTANCE, INTERNAL_UPDATE_PROGRESS_INTERVAL } from './modules/YoutubePlayer';
-import YoutubeViewWrapper from './YoutubeViewWrapper';
+import {
+ INTERNAL_SET_CONTROLLER_INSTANCE,
+ INTERNAL_UPDATE_PROGRESS_INTERVAL,
+} from './modules/YoutubePlayer';
import type { YoutubeViewProps } from './types/youtube';
+import YoutubeViewWrapper from './YoutubeViewWrapper';
function YoutubeView({ player, height, width, style, iframeStyle }: YoutubeViewProps) {
const { width: screenWidth } = useWindowDimensions();
diff --git a/packages/react-native-youtube-bridge/src/YoutubeViewWrapper.tsx b/packages/react-native-youtube-bridge/src/YoutubeViewWrapper.tsx
index 77437d7..075bf66 100644
--- a/packages/react-native-youtube-bridge/src/YoutubeViewWrapper.tsx
+++ b/packages/react-native-youtube-bridge/src/YoutubeViewWrapper.tsx
@@ -1,5 +1,11 @@
import type { ReactNode } from 'react';
-import { type DimensionValue, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native';
+import {
+ type DimensionValue,
+ type StyleProp,
+ StyleSheet,
+ View,
+ type ViewStyle,
+} from 'react-native';
type YoutubeViewWrapperProps = {
children: ReactNode;
diff --git a/packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts b/packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts
index 0551bdc..c756e57 100644
--- a/packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts
+++ b/packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts
@@ -1,5 +1,10 @@
+import {
+ type YoutubePlayerVars,
+ escapeHtml,
+ safeNumber,
+ validateVideoId,
+} from '@react-native-youtube-bridge/core';
import { useCallback } from 'react';
-import { type YoutubePlayerVars, escapeHtml, safeNumber, validateVideoId } from '@react-native-youtube-bridge/core';
import { youtubeIframeScripts } from './youtubeIframeScripts';
@@ -206,7 +211,19 @@ const useCreateLocalPlayerHtml = ({