CSP (Content-Security Policy) Build
In order for Alpine to execute JavaScript expressions from HTML attributes like x-on:click="console.log()"
, it needs to use utilities that violate the "unsafe-eval" Content Security Policy that some applications enforce for security purposes.
Under the hood, Alpine doesn't actually use eval() itself because it's slow and problematic. Instead it uses Function declarations, which are much better, but still violate "unsafe-eval".
Alpine offers an alternate build that doesn't violate "unsafe-eval" and supports most of Alpine's inline expression syntax.
Installation
You can use this build by either including it from a <script>
tag or installing it via NPM:
Via CDN
You can include this build's CDN as a <script>
tag just like you would normally with standard Alpine build:
<!-- Alpine's CSP-friendly Core --><script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
Via NPM
You can alternatively install this build from NPM for use inside your bundle like so:
npm install @alpinejs/csp
Then initialize it from your bundle:
import Alpine from '@alpinejs/csp' window.Alpine = Alpine Alpine.start()
Basic Example
Here's a working counter component using Alpine's CSP build. Notice how most expressions work exactly like regular Alpine:
<html> <head> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-a23gbfz9e'"> <script defer nonce="a23gbfz9e" src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script> </head> <body> <div x-data="{ count: 0, message: 'Hello' }"> <button x-on:click="count++">Increment</button> <button x-on:click="count = 0">Reset</button> <span x-text="count"></span> <span x-text="message + ' World'"></span> <span x-show="count > 5">Count is greater than 5!</span> </div> </body></html>
What's Supported
The CSP build supports most JavaScript expressions you'd want to use in Alpine:
Object and Array Literals
<!-- ✅ These work --><div x-data="{ user: { name: 'John', age: 30 }, items: [1, 2, 3] }"> <span x-text="user.name"></span> <span x-text="items[0]"></span></div>
Basic Operations
<!-- ✅ These work --><div x-data="{ count: 5, name: 'Alpine' }"> <span x-text="count + 10"></span> <span x-text="count > 3"></span> <span x-text="count === 5 ? 'Yes' : 'No'"></span> <span x-text="'Hello ' + name"></span> <div x-show="!loading && count > 0"></div></div>
Assignments and Updates
<!-- ✅ These work --><div x-data="{ count: 0, user: { name: '' } }"> <button x-on:click="count++">Increment</button> <button x-on:click="count = 0">Reset</button> <input x-model="user.name"></div>
Method Calls
<!-- ✅ These work --><div x-data="{ items: ['a', 'b'], getMessage: () => 'Hello' }"> <span x-text="getMessage()"></span> <button x-on:click="items.push('c')">Add Item</button></div>
Global Variables and Functions
<!-- ✅ These work --><div x-data="{ count: 42 }"> <button x-on:click="console.log('Count is:', count)">Log Count</button> <span x-text="Math.max(count, 100)"></span> <span x-text="parseInt('123') + count"></span> <span x-text="JSON.stringify({ value: count })"></span></div>
What's Not Supported
Some advanced JavaScript features aren't supported:
<!-- ❌ These don't work --><div x-data> <!-- Arrow functions --> <button x-on:click="() => console.log('hi')">Bad</button> <!-- Destructuring --> <div x-text="{ name } = user">Bad</div> <!-- Template literals --> <div x-text="`Hello ${name}`">Bad</div> <!-- Spread operator --> <div x-data="{ ...defaults }">Bad</div></div>
When to Extract Logic
While the CSP build supports simple inline expressions, you'll want to extract complex logic into dedicated functions or Alpine.data() components for better organization:
<!-- Instead of this --><div x-data="{ users: [] }" x-show="users.filter(u => u.active && u.role === 'admin').length > 0">
<!-- Do this --><div x-data="userManager" x-show="hasActiveAdmins"> <script nonce="..."> Alpine.data('userManager', () => ({ users: [], get hasActiveAdmins() { return this.users.filter(u => u.active && u.role === 'admin').length > 0 } }))</script>
This approach makes your code more readable, testable, and maintainable, especially for complex applications.
CSP Headers
Here's an example CSP header that works with Alpine's CSP build:
Content-Security-Policy: default-src 'self'; script-src 'nonce-[random]' 'strict-dynamic';
The key is removing 'unsafe-eval'
from your script-src
directive while still allowing your nonce-based scripts to run.
Code highlighting provided by Torchlight