I really love AlpineJS. It just got the right balance between ease of use & must-have JavaScript features. I like to think of it as a jQuery-alternative plus two-way bindings without the heavy load of a framework like Vue or React.
However, I still use a bundler (Rollup most of the time) to organize my code into modules. And since AlpineJS resides globally in the window
scope (one drawback of its simplicity) you can't bundle it up into single components as easily as in Vue, for example.
And because I like to organize my code into little chunks I'll show you the pattern I use to write my AlpineJS-Components:
Create the Main Entry-File
I use to call my main entry JavaScript-File main.js
or site.js
and it looks something like this:
// Import the all-mighty AlpineJS
import "alpinejs";
// Component to bootstrap our site
import App from "./components/App";
// import any components you might want to use now:
import { initNavigation } from "./components/Navigation";
import { initExampleComponent } from "./components/ExampleComponent";
// Start the app!
App(() => {
initNavigation();
initExampleComponent();
});
As you can see after importing alpine I import a main component called App
that is responsible for bootstrap and start all components. In my components, I only export one init-function that gets called in the App-Component's callback.
Create the App-Component
The App-Component looks like the following:
// components/App.js
export const App = fn => {
if (document.readyState != "loading") {
fn();
} else {
document.addEventListener("DOMContentLoaded", fn);
}
};
export default App;
Yeah, it's just as simple as it gets.
The App-Component takes only a callback function fn
as an argument which will then be called if the DOM is ready to handle our JavaScript code.
Our first AlpineJS Component
Then you can create your individual components like so:
// components/ExampleComponent.js
/**
* Initialize our component here!
*/
export const initExampleComponent = () => {
// First, check if this component has to be initialized
if (hasExampleComponent()) {
// then, fire it up!
start();
}
};
/**
* Checks if page has autocomplete component
* @return {Boolean}
*/
const hasExampleComponent = () => {
return document.getElementsByClassName("example-component").length > 0;
};
// Start our component
const start = () => {
// initialize your alpine component here into the window object
window.example = () => {
return {
isOpen: false,
// ... and so forth
};
};
};
I like this approach a lot because it is pretty transparent and you only "pollute" the main window
scope if the given component exists on the site. That might be unnecessary with, for example, a navigation component because you might want to render it on every page but I used this pattern many times for small components that were used only on a few pages. It just keeps my code tidy.
Do you like this pattern? Is it something you do already when using AlpineJS?
Oh, and hi there! 👋🏻 My name is Adrian and this is my very first post at dev.to 🎉
Top comments (1)
Don't know if getElementsByClassName is better or faster, but you could also detect the component by doing
That way, you don't have to rely on some arbitrary class name.