Skip to content

Nuxt Studio "content differs" warning caused by alphabetical key sorting in getOrderedSchemaKeys #378

@byrne-sbt

Description

@byrne-sbt

When using Nuxt Studio's visual editor with @nuxt/content v3, the editor shows the warning:

"The content on GitHub differs from your website version. Ensure your latest changes are deployed and refresh the page."

This occurs even when the deployed website is fully up to date with GitHub. The root cause is that getOrderedSchemaKeys() alphabetically sorts schema properties when building the SQLite table, so the API response returns keys in a different order than the source JSON file on GitHub.

Nuxt Studio compares the raw GitHub file (which preserves the original key order) against the website's API response (which returns keys alphabetically). Since the key order differs, Studio flags it as a content mismatch even though the actual data is identical.

Reproduction

1. Define a collection with a schema in content.config.ts

import { defineContentConfig, defineCollection, z } from '@nuxt/content';

const pageSeoSchema = z.object({
  seo_title: z.string(),
  seo_description: z.string(),
  seo_schema: z.string(),
});

export default defineContentConfig({
  collections: {
    landing_page: defineCollection({
      type: 'data',
      source: 'landing_page.json',
      schema: pageSeoSchema.extend({
        description: z.string(),
      }),
    }),
  },
});

2. Create the corresponding JSON file at content/landing_page.json

{
  "seo_title": "My Page Title",
  "seo_description": "My page description.",
  "seo_schema": "<script type=\"application/ld+json\">{}</script>",
  "description": "<p>Hello world</p>"
}

Note the key order matches the schema definition: seo_title, seo_description, seo_schema, description.

3. Fetch the data using queryCollection

<script setup lang="ts">
const { data } = await useAsyncData('landing', () =>
  queryCollection('landing_page').first()
);

// Log the key order returned by the API
console.log(Object.keys(data.value));
</script>

Expected key order (matching the source JSON and schema definition):
["seo_title", "seo_description", "seo_schema", "description"]

Actual key order (alphabetically sorted by getOrderedSchemaKeys):
["description", "seo_description", "seo_schema", "seo_title"]

The returned object has description as the first key instead of seo_title, even though both the JSON file and the Zod schema define seo_title first.

4. Deploy and open in Nuxt Studio
The visual editor shows the "content on GitHub differs" warning. Inspecting the comparison reveals:

  • GitHub version (raw file): first key is seo_title
  • Website version (API response): first key is description
  • The website API returns keys in alphabetical order: description, seo_description, seo_schema, seo_title.

Root Cause

The issue is in getOrderedSchemaKeys() in @nuxt/content/dist/runtime/internal/schema.js:

export function getOrderedSchemaKeys(schema) {
  const shape = Object.values(schema.definitions)[0]?.properties || {};
  const keys = new Set([
    shape.id ? "id" : void 0,
    shape.title ? "title" : void 0,
    ...Object.keys(shape).sort()   // <-- .sort() forces alphabetical order
  ].filter(Boolean));
  return Array.from(keys);
}

The .sort() call on line 15 forces all schema keys into alphabetical order. This function is used by both generateCollectionTableDefinition() (which creates the SQLite table with columns in sorted order) and generateCollectionInsert() (which writes data in sorted order). As a result, queryCollection() returns objects with alphabetically-ordered keys.

Nuxt Studio then compares this alphabetically-ordered API response against the raw JSON from GitHub (which preserves the author's original key order), and concludes the content has diverged.

Expected Behavior
The key order in the API response should match the key order in the source JSON file (or at least the schema definition order), so that Nuxt Studio's comparison does not produce a false positive mismatch.

One thing to add :

The preview module (preview/collection.js), which is what Nuxt Studio specifically uses for live editing, has its own generateCollectionInsert that also calls getOrderedSchemaKeys at line 90. This means the alphabetical sorting affects both the deployed website and the Studio preview pipeline.

// preview/collection.js line 86-91
function computeValuesBasedOnCollectionSchema(collection, data) {
  const fields = [];
  const values = [];
  const properties = collection.schema.definitions[collection.name].properties;
  const sortedKeys = getOrderedSchemaKeys(collection.schema);  // <-- same sort
  sortedKeys.forEach((key) => {

Suggested Fix
Remove the .sort() call from getOrderedSchemaKeys()?:

  const keys = new Set([
    shape.id ? "id" : void 0,
    shape.title ? "title" : void 0,
    ...Object.keys(shape)
  ].filter(Boolean));
  return Array.from(keys);
}

Object.keys() already returns keys in insertion order (per the ECMAScript spec), which is deterministic for a given schema definition. The sort is unnecessary for correctness and actively breaks the Nuxt Studio content comparison.

Alternatively, if deterministic ordering across different schema definitions is desired, the comparison logic in Nuxt Studio could be made key-order-agnostic (e.g., deep equality comparison ignoring key order).

Workaround
Reorder the top-level keys in all content JSON files to be alphabetical. This aligns the raw file with what the API returns, preventing the false mismatch. However, this requires manually maintaining alphabetical key order in every content file going forward.

Environment
@nuxt/content: 3.11.2
nuxt: 4.3.1
nuxt-studio: 1.4.0
Collection type: data (JSON)
Content storage: SQLite with sqliteConnector: 'native'

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions