--- order: 3 title: Extending --- # Extending Alpine has a very open codebase that allows for extension in a number of ways. In fact, every available directive and magic in Alpine itself uses these exact APIs. In theory you could rebuild all of Alpine's functionality using them yourself. ## Lifecycle concerns Before we dive into each individual API, let's first talk about where in your codebase you should consume these APIs. Because these APIs have an impact on how Alpine initializes the page, they must be registered AFTER Alpine is downloaded and available on the page, but BEFORE it has initialized the page itself. There are two different techniques depending on if you are importing Alpine into a bundle, or including it directly via a `
``` If you want to extract your extension code into an external file, you will need to make sure that file's `
``` ### Via an NPM module If you imported Alpine into a bundle, you have to make sure you are registering any extension code IN BETWEEN when you import the `Alpine` global object, and when you initialize Alpine by calling `Alpine.start()`. For example: ```js import Alpine from 'alpinejs' Alpine.directive('foo', ...) window.Alpine = Alpine window.Alpine.start() ``` Now that we know where to use these extension APIs, let's look more closely at how to use each one: ## Custom directives Alpine allows you to register your own custom directives using the `Alpine.directive()` API. ### Method Signature ```js Alpine.directive('[name]', (el, { value, modifiers, expression }, { Alpine, effect, cleanup }) => {}) ```   |   ---|--- name | The name of the directive. The name "foo" for example would be consumed as `x-foo` el | The DOM element the directive is added to value | If provided, the part of the directive after a colon. Ex: `'bar'` in `x-foo:bar` modifiers | An array of dot-separated trailing additions to the directive. Ex: `['baz', 'lob']` from `x-foo.baz.lob` expression | The attribute value portion of the directive. Ex: `law` from `x-foo="law"` Alpine | The Alpine global object effect | A function to create reactive effects that will auto-cleanup after this directive is removed from the DOM cleanup | A function you can pass bespoke callbacks to that will run when this directive is removed from the DOM ### Simple Example Here's an example of a simple directive we're going to create called: `x-uppercase`: ```js Alpine.directive('uppercase', el => { el.textContent = el.textContent.toUpperCase() }) ``` ```alpine
Hello World!
``` ### Evaluating expressions When registering a custom directive, you may want to evaluate a user-supplied JavaScript expression: For example, let's say you wanted to create a custom directive as a shortcut to `console.log()`. Something like: ```alpine
``` You need to retrieve the actual value of `message` by evaluating it as a JavaScript expression with the `x-data` scope. Fortunately, Alpine exposes its system for evaluating JavaScript expressions with an `evaluate()` API. Here's an example: ```js Alpine.directive('log', (el, { expression }, { evaluate }) => { // expression === 'message' console.log( evaluate(expression) ) }) ``` Now, when Alpine initializes the `
`, it will retrieve the expression passed into the directive ("message" in this case), and evaluate it in the context of the current element's Alpine component scope. ### Introducing reactivity Building on the `x-log` example from before, let's say we wanted `x-log` to log the value of `message` and also log it if the value changes. Given the following template: ```alpine
``` We want "Hello World!" to be logged initially, then we want "yolo" to be logged after pressing the ` ``` Now that accessing `$clipboard` returns a function itself, we can immediately call it and pass it an argument like we see in the template with `$clipboard('hello world')`. You can use the more brief syntax (a double arrow function) for returning a function from a function if you'd prefer: ```js Alpine.magic('clipboard', () => subject => { navigator.clipboard.writeText(subject) }) ``` ## Writing and sharing plugins By now you should see how friendly and simple it is to register your own custom directives and magics in your application, but what about sharing that functionality with others via an NPM package or something? You can get started quickly with Alpine's official "plugin-blueprint" package. It's as simple as cloning the repository and running `npm install && npm run build` to get a plugin authored. For demonstration purposes, let's create a pretend Alpine plugin from scratch called `Foo` that includes both a directive (`x-foo`) and a magic (`$foo`). We'll start producing this plugin for consumption as a simple `
``` Notice how our script is included BEFORE Alpine itself. This is important, otherwise, Alpine would have already been initialized by the time our plugin got loaded. Now let's look inside of `/js/foo.js`'s contents: ```js document.addEventListener('alpine:init', () => { window.Alpine.directive('foo', ...) window.Alpine.magic('foo', ...) }) ``` That's it! Authoring a plugin for inclusion via a script tag is extremely simple with Alpine. ### Bundle module Now let's say you wanted to author a plugin that someone could install via NPM and include into their bundle. Like the last example, we'll walk through this in reverse, starting with what it will look like to consume this plugin: ```js import Alpine from 'alpinejs' import foo from 'foo' Alpine.plugin(foo) window.Alpine = Alpine window.Alpine.start() ``` You'll notice a new API here: `Alpine.plugin()`. This is a convenience method Alpine exposes to prevent consumers of your plugin from having to register multiple different directives and magics themselves. Now let's look at the source of the plugin and what gets exported from `foo`: ```js export default function (Alpine) { Alpine.directive('foo', ...) Alpine.magic('foo', ...) } ``` You'll see that `Alpine.plugin` is incredibly simple. It accepts a callback and immediately invokes it while providing the `Alpine` global as a parameter for use inside of it. Then you can go about extending Alpine as you please.