CSP (Content-Security Policy) Build
In order for Alpine to be able to execute plain strings from HTML attributes as JavaScript expressions, for example x-on:click="console.log()"
, it needs to rely on utilities that violate the "unsafe-eval" Content Security Policy that some applications may 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".
In order to accommodate environments where this CSP is necessary, Alpine offer's an alternate build that doesn't violate "unsafe-eval", but has a more restrictive 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
To provide a glimpse of how using the CSP build might feel, here is a copy-pastable HTML file with a working counter component using a common CSP setup:
<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="counter"> <button x-on:click="increment"></button> <span x-text="count"></span> </div> <script nonce="a23gbfz9e"> document.addEventListener('alpine:init', () => { Alpine.data('counter', () => { return { count: 1, increment() { this.count++; }, } }) }) </script> </body></html>
API Restrictions
Since Alpine can no longer interpret strings as plain JavaScript, it has to parse and construct JavaScript functions from them manually.
Due to this limitation, you must use Alpine.data
to register your x-data
objects, and must reference properties and methods from it by key only.
For example, an inline component like this will not work.
<!-- Bad --><div x-data="{ count: 1 }"> <button @click="count++">Increment</button> <span x-text="count"></span></div>
However, breaking out the expressions into external APIs, the following is valid with the CSP build:
<!-- Good --><div x-data="counter"> <button @click="increment">Increment</button> <span x-text="count"></span></div>
Alpine.data('counter', () => ({ count: 1, increment() { this.count++ },}))
The CSP build supports accessing nested properties (property accessors) using the dot notation.
<!-- This works too --><div x-data="counter"> <button @click="foo.increment">Increment</button> <span x-text="foo.count"></span></div>
Alpine.data('counter', () => ({ foo: { count: 1, increment() { this.count++ }, },}))
Code highlighting provided by Torchlight