This episode covers using Cypress plugins and community modules, building custom plugins for test needs, extending custom commands and task APIs, and reusable plugin patterns across projects.

Every project has needs that Cypress does not cover out of the box. Episode 18 covers the plugin ecosystem: using community plugins, building custom plugins, extending commands and task APIs, and putting together reusable plugin patterns across projects.
Plugins are the line between a rigid suite and a suite built around the team's needs.
The Cypress plugin ecosystem is catalogued on the official site and npm. The installation pattern is consistent:
npm install -D cypress-file-uploadnpm install -D cypress-file-upload adds the upload plugin. Some plugins are commands imported in support; others are Node modules registered in the configuration.
Node-level plugins are registered through the setupNodeEvents block:
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
require("cypress-watch-and-reload")(on, config);
return config;
},
},
});setupNodeEvents(on, config) is the entry point for Node-side plugins. The on function is used by plugins to register event handlers; the config object may be modified and returned.
The most common custom plugin uses the task API — calling Node code from the browser:
module.exports = defineConfig({
e2e: {
setupNodeEvents(on) {
on("task", {
seedDatabase() {
return seedDatabase();
},
});
},
},
});on("task", { seedDatabase() { ... } }) registers a task named seedDatabase. This task can be called from tests with cy.task("seedDatabase") as we touched on in episode 10.
Here is a task you can use right away: returning a consistent value from the Node side:
on("task", {
timestampSekarang() {
return Date.now();
},
});timestampSekarang() returns the server time. Tasks like this let tests use a consistent value from the Node side, rather than from a browser that can be reset.
Cypress commands and the task API complement each other: commands run in the browser, tasks in Node. Combine both for powerful helpers:
Cypress.Commands.add("seedDanKunjungi", (fixtureName) => {
cy.task("seedDatabase", fixtureName);
cy.visit("/");
});cy.task("seedDatabase", fixtureName) sends an argument to the Node task. The seedDanKunjungi command packages the set up data then open the page pattern into a single call — a composition of command and task.
Tasks run in Node with access to the file system and environment. Limit what tasks can do — never accept arbitrary paths or commands from a spec:
on("task", {
readFixture(name) {
const whitelist = ["produk", "users"];
if (!whitelist.includes(name)) {
throw new Error("fixture tidak diizinkan");
}
return readFile(`cypress/fixtures/${name}.json`, "utf-8");
},
});whitelist.includes(name) limits the fixture names that may be read. Validating task input prevents a compromised spec from running dangerous operations in Node.
A good plugin can move between projects. Separate it as its own module:
const { defineConfig } = require("cypress");
const seedPlugin = require("./plugins/seed");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
seedPlugin(on, config);
return config;
},
},
});require("./plugins/seed") loads a separate plugin module. seedPlugin(on, config) registers tasks from that module — this pattern makes plugins shareable and testable on their own.
Give custom plugins a short documentation: what they register, the arguments they accept, and usage examples. If a plugin is used across projects, release it as an npm package with its own versioning so changes do not surprise other teams.
Info
Before writing a custom plugin, check whether the need already exists in a maintained community plugin. Writing your own is worthwhile for specific needs, but a mature general-purpose plugin saves time and maintenance.
Episode 18 opened the door to the ecosystem: using community plugins, building custom plugins through the task API and setupNodeEvents, extending commands and tasks, and putting together reusable plugin patterns with validation and documentation.
Key takeaways:
setupNodeEvents.cy.task.In the next episode, episode 19, we will cover operational readiness and runbooks — runbooks for flaky tests and environment drift, incident response for failing CI runs, maintaining test coverage and health, and team ownership and maintenance routines.