Alpine.js CSP Build for Strict Content Security Policy
Alpine CSP is a special build of Alpine.js that operates without the unsafe-eval CSP directive. Standard Alpine.js uses JavaScript's new Function() for HTML attribute expressions (e.g., :class="{'hidden': !open}"), which necessitates unsafe-eval in the Content Security Policy. The Alpine CSP build removes this requirement by restricting attribute content.
This page explains the differences between standard Alpine.js and Alpine CSP, and provides guidance for writing CSP-compatible components.
Alpine CSP Limitations
The Alpine CSP build supports a subset of standard Alpine features:
- Read-only property access: HTML attributes can only read component properties, not transform them
- Method execution: If a property is a function, it executes when referenced
- No inline expressions: Transformations like negation (
!open) or ternaries must be written as methods
While this requires more verbose code with many small methods, it eliminates the unsafe-eval attack vector that enables JavaScript injection attacks.
Alpine v2 Not Supported
There is no CSP build of Alpine v2. Themes using Alpine v2 must upgrade to Alpine v3 before implementing CSP compatibility.
To check your Alpine version, open the browser developer console on any page and run Alpine.version. If the version starts with "2", upgrade using the Hyvä Theme 1.2.0 upgrade notes.
Hyvä CSP Theme Versions
Hyvä provides both standard and CSP-compatible theme versions to accommodate different merchant needs:
- Standard Hyvä Theme: Uses regular Alpine.js with full expression support
- Hyvä Theme CSP: Uses Alpine CSP build, compatible with strict CSP
Both versions will be maintained for the foreseeable future, giving merchants and extension vendors time to migrate their code.
CSP Requirements Timeline
Starting April 1, 2025, PCI-DSS 4.0 requires strict CSP (without unsafe-eval) on payment-related pages. All checkout pages will require Alpine CSP compatibility.
Scope of CSP Requirements
The exact scope of "payment-related pages" in PCI-DSS remains ambiguous. Interpretation may depend on payment service provider requirements, merchant location, and whether in-context payment buttons (PayPal Express, Apple Pay) are used. Checkout pages definitively require strict CSP.
Hyvä Checkout CSP Support
Hyvä Checkout fully supports CSP strict mode. See the Hyvä Checkout CSP Documentation for configuration details.
Writing Alpine CSP-Compatible Code
Alpine CSP-compatible code works in both standard Alpine and Alpine CSP builds. Writing CSP-compatible components from the start ensures future compatibility and simplifies migration.
Key Differences from Standard Alpine
| Feature | Standard Alpine | Alpine CSP |
|---|---|---|
| Property reads | x-show="open" |
x-show="open" (same) |
| Negation | x-show="!open" |
x-show="isNotOpen" (method required) |
| Expressions | :class="{'hidden': !open}" |
:class="isHiddenClass" (method required) |
| Mutations | @click="open = false" |
@click="close" (method required) |
| Method arguments | @click="setTab('info')" |
@click="setTab" + data-tab="info" |
x-model |
Supported | Not supported |
x-for |
x-for="item in getItems('category')" |
x-for="item in items" (no method args) |
| Component constructor function | x-data="initCompoment()" or x-data="{open: false}" |
x-data="initCompoment" + constructor function registration with Alpine.data() |
Migration Examples
Negation: Convert inline negation to methods
<!-- Standard Alpine -->
<div x-show="!open">Hidden when open</div>
<!-- Alpine CSP -->
<div x-show="isClosed">Hidden when open</div>
Mutations: Move state changes to methods
<!-- Standard Alpine -->
<button @click="open = false">Close</button>
<!-- Alpine CSP -->
<button @click="close">Close</button>
Component constructors
<!-- Standard Alpine -->
<script>
function initMyComponent() { ... }
</script>
<div x-data="initMyComponent()">
<!-- Alpine CSP -->
<script>
function initMyComponent() { ... }
window.addEventListener(
'alpine:init',
() => Alpine.data('initMyComponent', initMyComponent)
)
</script>
<div x-data="initMyComponent">
Method Arguments: Use data attributes
<!-- Standard Alpine -->
<button @click="setTab('shipping')">Shipping</button>
<!-- Alpine CSP -->
<button @click="setTab" data-tab="shipping">Shipping</button>
Features That Work Identically
Most Alpine features work the same in both builds:
x-foriteration (without method arguments in the iterator expression)x-ifandx-showwith property references- Event handling with
@click,@input, etc. x-refreferencesx-initinitialization- Component lifecycle methods