Browser-sync features an extensible plugin architecture that allows developers to add custom functionality, integrate with build tools, and extend core capabilities. Plugins can hook into various lifecycle events and modify browser-sync behavior.
Register and load plugins to extend browser-sync functionality.
/**
* Register a plugin with browser-sync
* @param name - Plugin identifier name
* @param plugin - Plugin module or object
* @param callback - Optional callback for error handling
*/
use(name: string, plugin: BrowserSyncPlugin | PluginFunction, callback?: (error?: Error) => void): void;
/**
* Plugin configuration in browser-sync options
*/
plugins?: string[] | PluginConfig[];
interface PluginConfig {
/** Plugin module name or path */
module: string;
/** Plugin configuration options */
options?: any;
}
/**
* Standard plugin interface
*/
interface BrowserSyncPlugin {
/**
* Main plugin function called during initialization
* @param options - Plugin options passed during registration
* @param bs - Browser-sync instance
* @returns Plugin API or undefined
*/
plugin(options: any, bs: BrowserSyncInstance): any;
/** Optional plugin name */
pluginName?: string;
/** Optional plugin hooks */
hooks?: PluginHooks;
}
type PluginFunction = (options: any, bs: BrowserSyncInstance) => any;Usage Examples:
const browserSync = require('browser-sync');
// Register plugin after initialization
const bs = browserSync.create();
bs.init({ server: './app' });
// Register a simple plugin
bs.use('my-plugin', function(options, bs) {
console.log('Plugin loaded with options:', options);
// Plugin logic here
bs.emitter.on('file:changed', function(file) {
console.log('Plugin detected file change:', file);
});
});
// Register plugin with configuration
bs.use('custom-logger', {
plugin: function(options, bs) {
const logLevel = options.level || 'info';
bs.emitter.on('service:running', function(data) {
if (logLevel === 'debug') {
console.log('Service started:', data);
}
});
}
}, function(err) {
if (err) console.error('Plugin registration failed:', err);
});
// Load plugins via configuration
browserSync({
server: './app',
plugins: [
'bs-html-injector',
{
module: 'bs-eslint-message',
options: {
formatter: 'compact'
}
}
]
});Build custom plugins to extend browser-sync functionality for specific needs.
/**
* Plugin development interfaces
*/
interface PluginHooks {
/** Called when client connects */
'client:connected'?: (client: any) => void;
/** Called on file changes */
'file:changed'?: (file: string) => void;
/** Called before browser reload */
'browser:reload'?: () => void;
/** Called on service start */
'service:running'?: (data: any) => void;
}
interface BrowserSyncInstance {
/** Event emitter for listening to browser-sync events */
emitter: EventEmitter;
/** Socket.IO namespace for client communication */
sockets: any;
/** Get configuration option */
getOption(key: string): any;
/** Reload browsers */
reload(files?: any): void;
/** Send notification */
notify(message: string, timeout?: number): void;
}Plugin Development Examples:
// Simple logging plugin
const myLoggerPlugin = {
pluginName: 'MyLogger',
plugin: function(options, bs) {
const prefix = options.prefix || '[MyLogger]';
// Listen to various events
bs.emitter.on('file:changed', function(file) {
console.log(`${prefix} File changed: ${file}`);
});
bs.emitter.on('browser:reload', function() {
console.log(`${prefix} Browser reloading...`);
});
bs.emitter.on('client:connected', function(client) {
console.log(`${prefix} Client connected: ${client.id}`);
bs.notify(`New client connected: ${client.id}`);
});
// Return plugin API (optional)
return {
log: function(message) {
console.log(`${prefix} ${message}`);
}
};
}
};
// Register the plugin
bs.use('my-logger', myLoggerPlugin, { prefix: '[DEV]' });
// Build process integration plugin
const buildIntegrationPlugin = {
plugin: function(options, bs) {
const buildCommand = options.command || 'npm run build';
let isBuilding = false;
bs.emitter.on('file:changed', function(file) {
if (isBuilding) return;
// Check if file requires build
if (file.match(/\.(scss|ts|jsx?)$/)) {
isBuilding = true;
bs.notify('Building...', 1000);
const { exec } = require('child_process');
exec(buildCommand, (error, stdout, stderr) => {
isBuilding = false;
if (error) {
bs.notify(`Build failed: ${error.message}`, 5000);
} else {
bs.notify('Build complete');
bs.reload();
}
});
}
});
}
};
// API testing plugin
const apiTestPlugin = {
plugin: function(options, bs) {
const testEndpoint = options.endpoint || '/api/test';
// Add middleware for API testing
const middleware = function(req, res, next) {
if (req.url === testEndpoint) {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
status: 'ok',
timestamp: Date.now(),
userAgent: req.headers['user-agent']
}));
} else {
next();
}
};
// Return middleware to be used
return {
middleware: middleware
};
}
};Common browser-sync plugins for various development scenarios.
/**
* HTML injection plugin for faster HTML updates
*/
interface HtmlInjectorPlugin {
files: string | string[];
restrictions?: string[];
}
/**
* ESLint integration plugin for code quality notifications
*/
interface EslintMessagePlugin {
formatter?: string;
eslintrc?: string;
}
/**
* Compression plugin for gzip/deflate responses
*/
interface CompressionPlugin {
level?: number;
threshold?: number;
}Popular Plugin Examples:
const browserSync = require('browser-sync');
// HTML Injector - inject HTML changes without full reload
browserSync({
server: './app',
plugins: [
{
module: 'bs-html-injector',
options: {
files: './app/**/*.html',
restrictions: ['.navbar'] // Don't inject changes to navbar
}
}
]
});
// ESLint integration
browserSync({
server: './app',
plugins: [
{
module: 'bs-eslint-message',
options: {
formatter: 'compact'
}
}
]
});
// Multiple plugins with different configurations
browserSync({
server: './app',
plugins: [
'bs-html-injector', // Default configuration
{
module: 'bs-console-qrcode', // QR code for mobile access
options: {
ui: false // Don't show QR for UI
}
},
{
module: 'bs-latency',
options: {
latency: 100 // Simulate 100ms latency
}
}
]
});Understanding when and how plugins are executed in the browser-sync lifecycle.
/**
* Plugin lifecycle events
*/
interface PluginLifecycle {
/** Called when browser-sync initializes */
'init': (bs: BrowserSyncInstance) => void;
/** Called when server starts */
'service:running': (data: ServiceData) => void;
/** Called when client connects */
'client:connected': (client: ClientData) => void;
/** Called when file changes */
'file:changed': (file: string, event: string) => void;
/** Called before reload */
'browser:reload': (data?: any) => void;
/** Called on service exit */
'service:exit': () => void;
}
interface ServiceData {
port: number;
urls: {
local: string;
external: string;
ui: string;
};
}
interface ClientData {
id: string;
address: string;
userAgent: string;
}Lifecycle Hook Examples:
const lifecyclePlugin = {
plugin: function(options, bs) {
// Initialization hook
console.log('Plugin initializing...');
// Service hooks
bs.emitter.on('service:running', function(data) {
console.log('Service started on:', data.urls.local);
// Send welcome notification
setTimeout(() => {
bs.notify('Development server ready!', 3000);
}, 1000);
});
// Client connection hooks
bs.emitter.on('client:connected', function(client) {
console.log(`New client: ${client.id} from ${client.address}`);
// Track client for analytics
analytics.track('client_connected', {
userAgent: client.userAgent,
timestamp: Date.now()
});
});
// File change hooks
bs.emitter.on('file:changed', function(file, event) {
console.log(`File ${event}: ${file}`);
// Custom processing based on file type
const ext = path.extname(file);
switch (ext) {
case '.css':
// CSS-specific handling
console.log('Processing CSS file...');
break;
case '.js':
// JavaScript-specific handling
console.log('Processing JS file...');
break;
}
});
// Reload hooks
bs.emitter.on('browser:reload', function() {
console.log('Browsers reloading...');
// Clear caches, update counters, etc.
performance.mark('reload-start');
});
// Cleanup hooks
bs.emitter.on('service:exit', function() {
console.log('Plugin cleaning up...');
// Clean up resources, save state, etc.
cleanup();
});
}
};Advanced patterns for creating sophisticated browser-sync plugins.
Middleware Integration:
const middlewarePlugin = {
plugin: function(options, bs) {
// Create custom middleware
const customMiddleware = function(req, res, next) {
// Add custom headers
res.setHeader('X-Dev-Server', 'BrowserSync');
// Log requests
console.log(`${req.method} ${req.url}`);
// Custom routing
if (req.url.startsWith('/api/')) {
handleApiRequest(req, res);
} else {
next();
}
};
// Return middleware for browser-sync to use
return {
middleware: customMiddleware
};
}
};Socket Communication:
const socketPlugin = {
plugin: function(options, bs) {
// Listen for custom events from clients
bs.sockets.on('connection', function(socket) {
socket.on('custom:ping', function(data) {
console.log('Received ping from client:', data);
// Respond to client
socket.emit('custom:pong', {
message: 'Server received your ping',
timestamp: Date.now()
});
});
socket.on('custom:sync-data', function(data) {
// Broadcast to all clients
bs.sockets.emit('custom:data-update', data);
});
});
// Send periodic updates to clients
setInterval(() => {
bs.sockets.emit('custom:heartbeat', {
timestamp: Date.now(),
uptime: process.uptime()
});
}, 30000);
}
};