How to structure your Alpine.js Code into modules
Tags: alpinejs, javascript, tutorial, webdev
Published: 2021-05-01 15:38:42

I really love [AlpineJS](https://github.com/alpinejs/alpine). 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](https://vuejs.org/) or [React](https://reactjs.org/).

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:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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 🎉