Skip to content

Alpine JS

Add Alpine JS with TypeScript

From alpinejs.dev

Install alpinejs

sh
pnpm add @types/alpinejs alpinejs -D
ts
import type { Alpine as AlpineType } from "alpinejs";

/**
 * From https://bobbyhadz.com/blog/typescript-make-types-global
 */
declare global {
  const Alpine: AlpineType;
  interface Window {
    Alpine: AlpineType;
  }
}

export {};
ts
import Alpine from "alpinejs";

window.Alpine = Alpine;

Alpine.start();

In app vite.config.ts.

ts
export default defineConfig({
  // ...
  optimizeDeps: {
    include: ["alpinejs"],
  },
});

Modules

sh
mkdir resources/front/ts/modules

AlpineJS store example

sh
touch resources/front/ts/store-example.ts

Add to app.ts

ts
// ...

window.Alpine = Alpine;

Alpine.store("shop", {
  name: "Alpine-Shop",
  products: ["Swiss Alp Chocolate", "Car Alpine A110"],
});

Alpine.start();

In any Blade file.

html
<div x-data>
  <div x-text="$store.shop.name">shop-name</div>
  <div>
    Here you can buy:
    <ul>
      <template x-for="product in $store.shop.products">
        <li x-text="product"></li>
      </template>
    </ul>
  </div>
</div>

AlpineJS data example

sh
touch resources/front/ts/data-example.ts
ts
let refsAlpine: {
  text: HTMLElement;
};

const copy = () => ({
  copied: false,

  init() {
    // @ts-expect-error
    refsAlpine = this.$refs;
  },
  async copyText() {
    this.copied = true;
    let success = false;
    if (refsAlpine.text.textContent)
      await navigator.clipboard
        .writeText(refsAlpine.text.textContent)
        .then(() => (success = true));

    if (!success) console.error("Error on copy!");

    setTimeout(() => {
      this.copied = false;
    }, 3500);
  },
});

export default copy;

In app.ts

ts
// ...

window.Alpine = Alpine;

Alpine.data("copy", copy);

Alpine.start();

Custom module

sh
touch resources/front/ts/custom-module.ts
ts
export const customModule = () => {
  const customModule = "customModule";
  console.log(customModule);
};

In app.ts

ts
import Alpine from "alpinejs";
import { customModule } from "~/app/ts/custom-module";

customModule();

window.Alpine = Alpine;

Boiler plate modules

Color mode

sh
touch public/color-mode.js
js
const colorScheme = localStorage.getItem("color-scheme");

if (colorScheme) document.documentElement.classList.toggle(colorScheme, true);
else {
  const system =
    window.matchMedia &&
    window.matchMedia("(prefers-color-scheme: dark)").matches
      ? "dark"
      : "light";
  document.documentElement.classList.toggle(system, true);
}
sh
touch resources/front/ts/modules/color-mode.ts
ts
type Mode = "light" | "dark";
interface ModeElement {
  label: string;
  key: string;
}

const colorMode = () => ({
  mode: "light" as Mode,
  list: [
    {
      label: "Light",
      key: "light",
    },
    {
      label: "Dark",
      key: "dark",
    },
  ] as ModeElement[],
  key: "color-scheme",

  init() {
    this.setMode();
  },
  switchMode(mode: Mode) {
    const body = document.documentElement;
    this.list.forEach((element) => {
      body.classList.remove(element.key);
    });
    body.classList.add(mode);

    localStorage.setItem(this.key, mode);
    this.setMode(mode);
  },
  setMode(mode?: Mode) {
    const currentMode = localStorage.getItem(this.key);
    if (currentMode) {
      this.mode = currentMode as Mode;
    }
    if (mode === "light") {
      this.mode = "light";
    }
  },
});

export default colorMode;
ts
import colorMode from "./modules/color-mode";

window.Alpine = Alpine;

Alpine.data("colorMode", colorMode);

Alpine.start();
sh
touch resources/views/components/color-mode.blade.php
html
<div x-data="colorMode">
  <ul class="w-full space-y-1">
    <template x-for="element in list">
      <button
        type="button"
        x-text="element.label"
        :class="[mode === element.key ? 'bg-gray-100 dark:bg-gray-800' : '',
            'block w-full rounded-md px-2 py-1 text-left hover:bg-gray-100 dark:hover:bg-gray-800'
          ]"
        @click="switchMode(element.key)"
      ></button>
    </template>
  </ul>
</div>

MIT License