# Alpine.js JSON Encoding Best Practices

**Last Updated:** 2026-02-06

Best practices for embedding JSON data in Alpine.js `x-data` attributes to prevent syntax errors and ensure proper parsing.

## Problem

When embedding JSON data in Alpine.js `x-data` attributes, quotes in the JSON can break the HTML attribute if not properly escaped.

**Example Error:**
```
Uncaught SyntaxError: missing ) after argument list
Alpine Expression Error: Unexpected token ';'
```

**Root Cause:**
- `json_encode()` produces valid JSON with double quotes: `{"title":"Test"}`
- When placed directly in an HTML attribute, these quotes break the attribute: `x-data="blogIndexFilterData({"title":"Test"})"`
- The browser sees the first `"` after `{` as the end of the attribute, causing a syntax error
- Using `ENT_NOQUOTES` doesn't escape quotes, so the attribute breaks

## Solution

**Always use `ENT_QUOTES` when HTML-escaping JSON for Alpine.js `x-data` attributes:**

```php
// ✅ CORRECT
x-data="blogIndexFilterData(<?php echo htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8'); ?>)"

// ❌ WRONG
x-data="blogIndexFilterData(<?php echo htmlspecialchars(json_encode($data), ENT_NOQUOTES, 'UTF-8'); ?>)"
```

## Why ENT_QUOTES?

1. **Escapes quotes for HTML attributes** - Converts `"` to `&quot;` so the attribute doesn't break
2. **Browser automatically decodes HTML entities** - When JavaScript reads the attribute, `&quot;` becomes `"` automatically
3. **Also escapes `&`, `<`, `>`** - Ensures all HTML special characters are properly escaped
4. **JavaScript receives valid JSON** - After browser decoding, JavaScript parses the JSON correctly

## Examples

### Blog Index Page

**File**: `v2/pages/blog/index.php`

```php
<section 
    x-data="blogIndexFilterData(<?php echo htmlspecialchars(json_encode($all_posts), ENT_QUOTES, 'UTF-8'); ?>, <?php echo htmlspecialchars(json_encode($categories), ENT_QUOTES, 'UTF-8'); ?>, <?php echo intval($posts_per_page); ?>)"
    x-init="init()"
    class="blog-posts-section">
```

### Blog TOC Component

**File**: `v2/components/blog/BlogTOC.php`

```php
// Convert PHP array to JSON for Alpine.js (escape for HTML attributes)
// Use ENT_QUOTES to escape quotes for HTML attributes - browser will decode &quot; back to " before JavaScript parses
$toc_items_json = htmlspecialchars(json_encode($toc_items), ENT_QUOTES, 'UTF-8');
```

```php
<div 
    x-data="blogTOC(<?php echo $toc_items_json; ?>)"
    x-show="showTOC"
    x-cloak>
```

## Testing

**Test with content containing special characters:**

```php
$test_data = [
    'title' => 'Test & Example',
    'description' => 'Content with <tags> and "quotes"'
];

// Verify encoding
$json = json_encode($test_data);
$escaped = htmlspecialchars($json, ENT_QUOTES, 'UTF-8');

// Should output: {&quot;title&quot;:&quot;Test &amp; Example&quot;,&quot;description&quot;:&quot;Content with &lt;tags&gt; and \&quot;quotes\&quot;&quot;}
// Browser will decode &quot; back to " and &amp; back to & when reading attribute
// JavaScript will then parse the JSON correctly
```

**Validation Checklist:**

- [ ] No syntax errors in browser console
- [ ] Alpine.js data parses correctly
- [ ] Content with `&` characters displays correctly
- [ ] Content with `<`, `>` characters displays correctly
- [ ] Content with quotes displays correctly

## Common Pitfalls

### ❌ Not Escaping Quotes (Breaks HTML Attribute)

```php
// WRONG - Quotes break the HTML attribute
htmlspecialchars(json_encode($data), ENT_NOQUOTES, 'UTF-8')
// Result: x-data="blogIndexFilterData({"title":"Test"})" - quotes break attribute
```

### ❌ Not Escaping HTML Entities

```php
// WRONG - & characters not escaped
json_encode($data) // Direct output without htmlspecialchars
// Result: {"title":"Test & Example"} - & breaks HTML attribute parsing
```

### ✅ Correct Pattern

```php
// CORRECT - Escapes quotes, &, <, > for HTML attributes
htmlspecialchars(json_encode($data), ENT_QUOTES, 'UTF-8')
// Result: x-data="blogIndexFilterData({&quot;title&quot;:&quot;Test &amp; Example&quot;})"
// Browser decodes &quot; to " and &amp; to & before JavaScript parses
```

## Related Files

- `v2/pages/blog/index.php` - Blog index with Alpine.js filtering
- `v2/components/blog/BlogTOC.php` - Table of contents component
- `v2/base/include_tools_filters.php` - Tools filter component (uses `htmlspecialchars()` for category names)

## Browser Behavior

**How browsers handle HTML entities in attributes:**

1. **HTML Parser**: Reads `x-data="blogIndexFilterData({&quot;title&quot;:&quot;Test &amp; Example&quot;})"`
2. **Entity Decoding**: Automatically converts `&quot;` to `"` and `&amp;` to `&` when reading attribute value
3. **JavaScript**: Receives decoded value: `blogIndexFilterData({"title":"Test & Example"})`
4. **JSON Parsing**: JavaScript parses JSON correctly with all special characters properly decoded

**This is why `ENT_QUOTES` works correctly** - the browser handles entity decoding automatically before JavaScript evaluates the expression.

## Edge Cases

### Content with HTML Entities

If your data already contains HTML entities (e.g., `&amp;` in the source), `json_encode()` will escape them as `\"&amp;\"` in the JSON string. When HTML-escaped, this becomes `&quot;&amp;&quot;` which the browser will decode correctly.

### Content with Unicode Characters

`json_encode()` handles Unicode correctly. `htmlspecialchars()` with UTF-8 encoding preserves Unicode characters.

### Very Large JSON Objects

For very large JSON objects (>100KB), consider:
- Using `data-*` attributes instead of inline `x-data`
- Loading data via AJAX/fetch
- Using Alpine.js `Alpine.data()` for reusable components

## References

- [Alpine.js Documentation](https://alpinejs.dev/)
- [PHP json_encode()](https://www.php.net/manual/en/function.json-encode.php)
- [PHP htmlspecialchars()](https://www.php.net/manual/en/function.htmlspecialchars.php)
- [HTML Entity Encoding](https://developer.mozilla.org/en-US/docs/Glossary/Entity)
