Creating Custom Svelte Directives
Svelte directives are a special syntax used to control DOM operations within components. In addition to built-in directives (e.g., bind, on, if, each), Svelte allows you to create custom directives to extend the framework’s functionality. Custom directives enable you to encapsulate complex DOM operations, making them reusable across multiple components while keeping the code clear and modular.
Basic Structure of Custom Directives
A custom directive is typically a function that accepts the following parameters:
node: The DOM node to which the directive is applied.binding: An object containing the directive’s value and possible modifiers.attributes: An object containing attributes and events related to the directive.context: The current component’s context, which can be used to access the component instance.
Creating a Custom Directive
Let’s create a simple custom directive to highlight text nodes.
// highlight.directive.js
export default function highlight({ value }) {
return {
// Called when the directive is applied to the node
create: (node) => {
node.style.backgroundColor = value || 'yellow';
},
// Called when the directive’s value changes
update: (node, newValue) => {
node.style.backgroundColor = newValue || 'yellow';
},
// Called when the directive is removed from the node
destroy: (node) => {
node.style.backgroundColor = '';
}
};
}
Using a Custom Directive in a Component
Next, we’ll use this custom directive in a Svelte component.
<!-- HighlightText.svelte -->
<script>
import highlight from './highlight.directive.js';
let text = 'Hello, Svelte!';
</script>
<p use:highlight={text}>This text will be highlighted.</p>
Advanced Usage of Custom Directives
Custom directives can also accept modifiers, making them more flexible.
// toggleVisibility.directive.js
export default function toggleVisibility({ modifiers }) {
return {
create: (node) => {
node.style.display = 'block';
},
update: (node) => {
if (modifiers.includes('hidden')) {
node.style.display = 'none';
} else {
node.style.display = 'block';
}
},
destroy: (node) => {
node.style.display = '';
}
};
}
Using the Directive in a Component:
<!-- ToggleVisibility.svelte -->
<script>
let isVisible = true;
</script>
<p use:toggleVisibility={{hidden: !isVisible}}>This text can be hidden.</p>
Lifecycle Methods of Custom Directives
Custom directives have three primary lifecycle methods:
create: Called when the directive is first applied to a node.update: Called when the directive’s value or modifiers change.destroy: Called when the directive is removed from the node.
These methods allow you to perform operations at different stages of the directive’s lifecycle.
Debugging and Testing Custom Directives
To debug custom directives, you can use Svelte’s debug directive to inspect their execution. In development mode, the debug directive logs information about the directive’s invocation to the console.
<!-- Debugging.svelte -->
<script>
import highlight from './highlight.directive.js';
</script>
<p use:highlight={true} use:debug>This text will be highlighted and debugged.</p>
Developing Svelte Plugins to Extend Functionality
Svelte plugins are a way to extend the functionality of the Svelte compiler, enabling the addition of custom syntax, preprocessors, postprocessors, or other features needed during the compilation process. By developing Svelte plugins, you can tailor Svelte’s behavior to meet specific project requirements or workflows.
Basic Structure of Svelte Plugins
A Svelte plugin is a function that returns an object containing methods such as:
transform(code, id): Modifies the source code of Svelte components.codeis the component’s source code string, andidis the file path of the component.load(id): Loads content for non-Svelte files, such as those handled by preprocessors.resolveId(importee, importer): Resolves module import paths.buildStart(): Called when the build process starts.buildEnd(): Called when the build process ends.
Creating Your First Svelte Plugin
Let’s create a simple plugin that replaces all instances of “Hello” with “Hi” in component code.
// hello-to-hi.plugin.js
export default function helloToHi() {
return {
transform: (code, id) => {
if (id.endsWith('.svelte')) {
return {
code: code.replace(/Hello/g, 'Hi'),
map: null // Source map
};
}
}
};
}
Using a Svelte Plugin in a Project
To use a plugin in a Svelte project, add it to the build configuration. If you’re using Rollup, you can include the plugin in the rollup.config.js file.
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import helloToHi from './hello-to-hi.plugin.js';
export default {
input: 'src/main.js',
output: {
file: 'public/build/bundle.js',
format: 'iife',
sourcemap: true
},
plugins: [
svelte(),
helloToHi()
]
};
Advanced Usage of Svelte Plugins
Plugins can perform more complex operations, such as adding custom preprocessors, postprocessors, or even modifying Svelte’s syntax.
Custom Preprocessor
// markdown-preprocessor.plugin.js
import fs from 'fs';
export default function markdownPreprocessor() {
return {
load: (id) => {
if (id.endsWith('.md')) {
return import('markdown-it').then(({ default: markdownIt }) => {
const md = new markdownIt();
return md.render(fs.readFileSync(id, 'utf-8'));
});
}
}
};
}
Custom Postprocessor
// css-postprocessor.plugin.js
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
export default function cssPostprocessor() {
return {
transform: async (code, id) => {
if (id.endsWith('.css')) {
const result = await postcss([autoprefixer]).process(code, { from: undefined });
return {
code: result.css,
map: null // Source map
};
}
}
};
}
Plugin Lifecycle Methods
Plugins can define buildStart() and buildEnd() methods, which are executed at the start and end of the build process, respectively.
// lifecycle.plugin.js
export default function lifecyclePlugin() {
return {
buildStart() {
console.log('Build started');
},
buildEnd() {
console.log('Build ended');
}
};
}



