videojs/video.js · error · Error

Plugin must be sub-classed; not directly instantiated.

Error message

Plugin must be sub-classed; not directly instantiated.

What it means

The Plugin base class is abstract: it sets up evented state, logging, and version tracking that only make sense on a subclass with a name and per-instance disposal. Direct instantiation (new Plugin(player)) would lack these and break plugin lifecycle hooks, so the constructor refuses.

Source

Thrown at src/js/plugin.js:200

 * @fires   Player#pluginsetup:$name
 * @listens Player#dispose
 * @throws  {Error}
 *          If attempting to instantiate the base {@link Plugin} class
 *          directly instead of via a sub-class.
 */
class Plugin {

  /**
   * Creates an instance of this class.
   *
   * Sub-classes should call `super` to ensure plugins are properly initialized.
   *
   * @param {Player} player
   *        A Video.js player instance.
   */
  constructor(player) {
    if (this.constructor === Plugin) {
      throw new Error('Plugin must be sub-classed; not directly instantiated.');
    }

    this.player = player;

    if (!this.log) {
      this.log = this.player.log.createLogger(this.name);
    }

    // Make this object evented, but remove the added `trigger` method so we
    // use the prototype version instead.
    evented(this);
    delete this.trigger;

    stateful(this, this.constructor.defaultState);
    markPluginAsActive(player, this.name);

    // Auto-bind the dispose method so we can use it as a listener and unbind
    // it later easily.

View on GitHub (pinned to c3a7e0e6d2)

Solutions

  1. Define a subclass: class MyPlugin extends videojs.getPlugin('plugin') { ... } and register it, then use player.myPlugin().
  2. Never instantiate the base Plugin; use registerPlugin(name, factory) for basic plugins instead.
  3. If you only need a basic plugin, register a plain function via videojs.registerPlugin(name, fn).

Example fix

// before
const Plugin = videojs.getPlugin('plugin');
new Plugin(player); // throws
// after
class MyPlugin extends videojs.getPlugin('plugin') {}
videojs.registerPlugin('myPlugin', MyPlugin);
player.myPlugin();
Defensive patterns

Strategy: type-guard

Validate before calling

function instantiatePlugin(Klass, player) {
  const Base = videojs.getPlugin('plugin');
  if (Klass === Base) throw new TypeError('instantiate a Plugin subclass, not the base');
  return new Klass(player);
}

Type guard

const isPluginSubclass = (K) =>
  typeof K === 'function' && K !== videojs.getPlugin('plugin') &&
  videojs.getPlugin('plugin').prototype.isPrototypeOf(K.prototype);

Prevention

When it happens

Trigger: Calling new videojs.getPlugin('plugin')(player); mistakenly instantiating the base Plugin returned by registerPlugin or getPlugin('plugin').

Common situations: Confusing getPlugin('plugin') (the base class) with a named plugin; copy-paste from docs that show Plugin extension without the extends clause; SSR/inspection code that tries to instantiate every plugin.

Related errors


AI-assisted analysis of videojs/video.js@c3a7e0e6d2 (2026-08-13). Data as JSON: /api/errors/d207425755c7e77c. Report an issue: GitHub.