npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, πŸ‘‹, I’m Ryan HefnerΒ  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you πŸ™

Β© 2026 – Pkg Stats / Ryan Hefner

alpinejs-tash

v2.0.1

Published

Alpine JS plugin that enables mustache-like templating syntax for simpler data binding and rendering in your Alpine JS applications πŸš€

Readme

Alpine JS Tash

Render Alpine JS data with {curly braces} instead of x-text πŸš€

Alpine JS Tash (Template Hash) brings the interpolation syntax you already know from React, Vue, Svelte and Angular to Alpine. Write the value where it belongs β€” in the middle of a sentence β€” instead of splitting the sentence into <span>s.

<p x-tash>Hello, I am {name} and I am {age} years old!</p>

Against the x-text equivalent:

<p>Hello, I am <span x-text="name"></span> and I am <span x-text="age"></span> years old!</p>

Features

  • πŸ” Keys are detected from the template β€” no need to list them
  • πŸ” Works inside x-for and x-if
  • 🏷️ Works in text and in attributes
  • 🎨 Configurable delimiters, so it won't fight Blade, Twig or Jinja
  • πŸ”„ Fully reactive, and updates without rebuilding your DOM
  • πŸ›‘οΈ Values render as text, so data can't inject markup
  • πŸͺΆ ~1.5KB gzipped, zero dependencies

Install

CDN

<script defer src="https://unpkg.com/alpinejs-tash@latest/dist/cdn.min.js"></script>

<script defer src="https://unpkg.com/alpinejs@latest/dist/cdn.min.js"></script>

Tash must load before Alpine, as with every Alpine plugin.

Package Manager

pnpm add -D alpinejs-tash

yarn add -D alpinejs-tash

npm install -D alpinejs-tash
import Alpine from 'alpinejs'
import tash from 'alpinejs-tash'

Alpine.plugin(tash)

window.Alpine = Alpine

Alpine.start()

Usage

Add x-tash to an element and use {key} anywhere inside it:

<div x-data="{ name: 'Walter White', age: 50, company: 'Gray Matter Technologies' }">
  <p x-tash>
    Hello, I am {name}! I am {age} years old and I currently work at {company}!
  </p>

  <!-- Hello, I am Walter White! I am 50 years old and I currently work at Gray Matter Technologies! -->
</div>

Anything that works in an Alpine expression works as a key, including nested paths, array indexes and getters:

<p x-tash>{user.name} has {user.orders.length} orders, worth {orderTotal}</p>

Listing keys explicitly

Passing a comma-separated list restricts rendering to those keys, and leaves every other {...} in the template untouched. This is the v1 syntax, and it still works:

<p x-tash="name, age">Hi {name}, you are {age}. This stays literal: {company}</p>

Use it when your markup contains literal braces you don't want touched. Otherwise leave x-tash empty and let it find the keys.

Attributes

Placeholders in attributes are interpolated too:

<div x-data="{ label: 'Close dialog', theme: 'dark' }">
  <button x-tash title="{label}" data-theme="{theme}">Γ—</button>

  <!-- <button title="Close dialog" data-theme="dark">Γ—</button> -->
</div>

Alpine's own attributes β€” anything starting with x-, @ or : β€” are skipped, because Alpine already evaluates those as expressions. Use :title="label" for those, not title="{label}".

Loops and conditionals

Placeholders inside x-for and x-if work, and resolve against the scope they sit in:

<ul x-tash x-data="{ rows: [{ label: 'a', n: 1 }] }">
  <template x-for="row in rows">
    <li title="row {row.label}">{row.label} = {row.n}</li>
  </template>
</ul>

Content inserted after init is picked up as it appears, so rows added later render too. Putting x-tash on the element inside the loop works equally well if you prefer to scope it tightly:

<template x-for="item in items">
  <li x-tash>Item: {item}</li>
</template>

Delimiters

The default pair is { and }. Change it globally when it collides with server-side templating β€” Blade, Twig, Jinja and Handlebars all use {{ }}:

import Alpine from 'alpinejs'
import tash from 'alpinejs-tash'

Alpine.plugin(tash({ delimiters: ['[[', ']]'] }))
<p x-tash>Hello, [[name]]! Blade's {{ name }} is left alone.</p>

From a CDN, set the config before the plugin script runs:

<script>
  window.tashConfig = { delimiters: ['[[', ']]'] }
</script>

<script defer src="https://unpkg.com/alpinejs-tash@latest/dist/cdn.min.js"></script>

<script defer src="https://unpkg.com/alpinejs@latest/dist/cdn.min.js"></script>

Whitespace inside the delimiters is always optional, so [[name]] and [[ name ]] are equivalent. To use Vue or Angular style, set the pair to ['{{', '}}'].

How Values Render

| Value | Renders as | | -------------------- | ----------------------------- | | 'Walter' | Walter | | 50 | 50 | | null / undefined | (empty string) | | { a: 1 } | {"a":1} | | '<b>bold</b>' | <b>bold</b> as visible text |

Values are written as text, never parsed as HTML. A value containing markup shows up as characters on the page, so user-supplied data can't inject elements. If you genuinely want to render HTML, use Alpine's x-html β€” and only on content you trust.

Notes & Limitations

Each node is bound once. Tash captures a node's original text the first time it sees it, then writes rendered output back into that same node. Editing the DOM by hand to add new {key} text won't be picked up, but anything Alpine inserts β€” x-for rows, x-if branches β€” is.

Updates preserve your DOM. Because only matched nodes are rewritten, child elements keep their identity across updates β€” a form input keeps its value, focus isn't lost, event listeners survive, and nested x-data components aren't torn down and rebuilt.

An unresolvable key only affects its own placeholder. Keys are evaluated independently, so {config} with no config in scope is left on the page as literal text while every other placeholder still renders. Alpine logs its usual expression error for it. If a literal {...} in your copy is triggering that noise, name your real keys explicitly to opt the rest out.

Nested x-tash elements own their own subtree, and are skipped by the parent, so a placeholder is never rendered twice.

<script> and <style> contents are never interpolated.

Breaking Changes in 2.0.0

  • Values render as text, not HTML. v1 replaced into el.innerHTML, so a value containing markup became real elements. If you relied on that, switch that binding to x-html.
  • The key list is now optional, and x-tash with no expression auto-detects keys. Existing x-tash="a, b" markup keeps working unchanged.
  • The .vue and .angular modifiers are gone, replaced by the delimiters option. Both were fixed to {{ }}, which is exactly the pair that collides with server-side templating; configuring it globally covers those two styles and every other pair. Replace x-tash.vue / x-tash.angular with x-tash and Alpine.plugin(tash({ delimiters: ['{{', '}}'] })).
  • null and undefined render as an empty string rather than the text null / undefined, and objects render as JSON rather than [object Object].
  • Placeholders in attributes now render. Previously only element content was processed. If you had a literal {...} in an attribute on an x-tash element, it will now be interpolated.
  • dist/esm.min.js is gone, replaced by dist/module.mjs and dist/module.cjs behind an exports map β€” 1.2.1 declared only module, so neither require() nor Node ESM import resolved. Package-name imports are unaffected; update any deep imports of the old path. dist/cdn.min.js is unchanged, so CDN users need no changes.