Skip to content

DataCloneError when visiting pages with Vue reactive data in Inertia.js v2.0 #2490

@caredev

Description

@caredev

@inertiajs/vue3 Version

2.0.17

Backend stack (optional)

  • Inertia.js Vue3 Adapter: ^2.0.17
  • Inertia Laravel: ^2.0.4
  • Vue: ^3.5.18
  • Laravel: ^12.0
  • PHP: ^8.3

Related Issues

This appears to be a regression of issue #775, which was resolved in v1.x but has returned in v2.0 due to the new client-side routing architecture not properly handling Vue

Describe the problem

After upgrading to Inertia.js v2.0, visiting pages that contain Vue reactive data results in a DataCloneError when the History API is called. This appears to be related to v2.0's new client-side routing mechanism directly passing Vue reactive Proxy objects to History.replaceState().

Error

Uncaught (in promise) DataCloneError: Failed to execute 'replaceState' on 'History': #<Object> could not be cloned. at History.doReplaceState (history.ts:185:20) at doReplace (history.ts:164:16) at history.ts:171:11

Possible cause

Vue 3's reactive system wraps objects in Proxy instances that cannot be serialized by the browser's structuredClone algorithm used by the History API. In v2.0, these reactive objects are being passed directly to History.replaceState() without being cleaned of their reactivity.

Minimal Reproduction

Laravel Controller:

<?php
class ProductController extends Controller
{
    public function index(Request $request)
    {
        return Inertia::render('Products/Index', [
            'data' => [
                'items' => Product::paginate(10),
                'filters' => [...], // Any nested data structure
            ]
        ]);
    }
}

Vue component

<template>
  <div>
    <h1>Products</h1>
    <!-- Any component that uses the reactive data -->
  </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  data: Object
})

// Any reactive computation using the props
const items = computed(() => props.data.items)
</script>

Expected behaviour

Pages should load without DataCloneError, as they did in Inertia.js v1.x.

Steps to reproduce

  1. Create a page with reactive data from Laravel controller
  2. Visit the page directly (e.g., /products)
  3. Error occurs on initial page load

Adopted temporal workaround

// app.js - before Vue imports
import { toRaw } from 'vue'

const originalReplaceState = History.prototype.replaceState;
const originalPushState = History.prototype.pushState;

function deepCleanReactivity(obj) {
  if (obj === null || obj === undefined || typeof obj !== 'object') {
    return obj;
  }
  
  const rawObj = toRaw(obj);
  
  if (Array.isArray(rawObj)) {
    return rawObj.map(deepCleanReactivity);
  }
  
  const cleaned = {};
  for (const [key, value] of Object.entries(rawObj)) {
    cleaned[key] = deepCleanReactivity(value);
  }
  return cleaned;
}

History.prototype.replaceState = function(state, title, url) {
  const cleanedState = deepCleanReactivity(state);
  return originalReplaceState.call(this, cleanedState, title, url);
};

History.prototype.pushState = function(state, title, url) {
  const cleanedState = deepCleanReactivity(state);
  return originalPushState.call(this, cleanedState, title, url);
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    vue 3Related to the vue 3 adapter

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions