장난감 연구소

[React] BlockNote Video.js 적용 본문

개발/React

[React] BlockNote Video.js 적용

changi1122 2026. 4. 11. 01:23

    개요

    Notion 스타일 React 블록 기반 편집기인 BlockNote에 Video.js v10을 적용하는 코드입니다.

    BlockNote에서 동영상을 첨부하였을 때 플레이어를 Video.js로 변경합니다.

     

     

    버전

    React 19+ (required for Video.js v10)
    @blocknote/* ^0.45.0
    @videojs/react ^10.0.0-beta.15

     

    코드

     

    toys/blocknote-videojs-block at master · changi1122/toys

    Contribute to changi1122/toys development by creating an account on GitHub.

    github.com

     

    VideoPlayer.tsx

    import { createReactBlockSpec, FileBlockWrapper } from "@blocknote/react";
    import { MdVideocam } from "react-icons/md";
    import { VideoPlayer } from "../VideoPlayer";
    
    const VideoBlockComponent = (props: any) => {
      if (props.block.props.url) {
        return (
          <VideoPlayer src={props.block.props.url} />
        );
      }
      return (
        <FileBlockWrapper {...props} buttonIcon={<MdVideocam size={24} />} />
      );
    };
    
    export const createVideoBlock = createReactBlockSpec(
      {
        type: "video",
        propSchema: {
          url: { default: "" },
          name: { default: "" },
          caption: { default: "" },
          showPreview: { default: true },
          previewWidth: { default: undefined, type: "number" as const },
        },
        content: "none",
      },
      {
        meta: { fileBlockAccept: ["video/*"], selectable: false },
        render: VideoBlockComponent,
      }
    );

     

    VideoBlock.tsx

    import { createReactBlockSpec, FileBlockWrapper } from "@blocknote/react";
    import { MdVideocam } from "react-icons/md";
    import { VideoPlayer } from "../VideoPlayer";
    
    const VideoBlockComponent = (props: any) => {
      if (props.block.props.url) {
        return (
          <VideoPlayer src={props.block.props.url} />
        );
      }
      return (
        <FileBlockWrapper {...props} buttonIcon={<MdVideocam size={24} />} />
      );
    };
    
    export const createVideoBlock = createReactBlockSpec(
      {
        type: "video",
        propSchema: {
          url: { default: "" },
          name: { default: "" },
          caption: { default: "" },
          showPreview: { default: true },
          previewWidth: { default: undefined, type: "number" as const },
        },
        content: "none",
      },
      {
        meta: { fileBlockAccept: ["video/*"], selectable: false },
        render: VideoBlockComponent,
      }
    );

     

    BlockNoteEditor.tsx

    import { useCreateBlockNote } from "@blocknote/react";
    import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core";
    import type { PartialBlock } from "@blocknote/core";
    import { BlockNoteView } from "@blocknote/mantine";
    import { codeBlockOptions } from "@blocknote/code-block";
    import { createCodeBlockSpec } from "@blocknote/core";
    import { ko } from "@blocknote/core/locales";
    
    import { createVideoBlock } from "./blocks/VideoBlock";
    import "@blocknote/mantine/style.css";
    import "@blocknote/core/fonts/inter.css";
    
    const schema = BlockNoteSchema.create({
      blockSpecs: {
        ...defaultBlockSpecs,
        codeBlock: createCodeBlockSpec(codeBlockOptions),
        video: createVideoBlock(),
      },
    });
    
    interface BlockNoteEditorProps {
      editable?: boolean;
      body?: PartialBlock[];
      onChange?: (blocks: PartialBlock[]) => void;
      uploadFile?: (file: File) => Promise<string>;
    }
    
    const BlockNoteEditor = ({
      editable = true,
      body,
      onChange,
      uploadFile,
    }: BlockNoteEditorProps) => {
      const editor = useCreateBlockNote({
        initialContent: Array.isArray(body) ? body : undefined,
        schema,
        dictionary: ko,
        uploadFile,
      });
    
      const handleOnChange = () => {
        if (onChange) {
          onChange(editor.document);
        }
      };
    
      return (
        <BlockNoteView
          editor={editor}
          theme="light"
          editable={editable}
          onChange={handleOnChange}
        />
      );
    };
    
    export default BlockNoteEditor;