Skip to content

[Bug]: New field dialog rejects camelCase field IDs, but Instatic's own built-in fields are camelCase #434

Description

@gentledepp

Summary

The New field dialog in the Data workspace rejects camelCase field IDs, but Instatic's own built-in post-type fields are camelCase. So a post type ends up carrying two naming conventions at once, and nothing below the dialog requires the restriction.

src/admin/pages/data/components/NewFieldDialog/newFieldDialogModel.ts:

const FIELD_ID_PATTERN = /^[a-z][a-z0-9_]*$/

export function fieldIdError(id: string, existingIds: string[]): string | null {
  if (!id) return null
  if (!FIELD_ID_PATTERN.test(id)) return 'Must start with a lowercase letter; use letters, numbers, underscores only.'
  ...
}

fieldIdError gates canCreate in NewFieldDialog.tsx, so a camelCase ID blocks creation outright rather than being normalized.

Meanwhile, in src/core/data/schemas.ts:

export const POST_TYPE_FIELD_FEATURED_MEDIA = 'featuredMedia'
export const POST_TYPE_FIELD_SEO_TITLE = 'seoTitle'
export const POST_TYPE_FIELD_SEO_DESCRIPTION = 'seoDescription'

…and the storage schema itself has no opinion — FieldCommonProps declares id: Type.String(), and normalizeDataTableFields only filters against DataFieldSchema. The restriction lives in the dialog and nowhere else.

Why it matters in practice. Migrating a real site, I planned firmaUrl, interviewUrl, aufStartseite and got firmaurl, interviewurl, aufstartseite — while the same records' built-ins stayed featuredMedia and seoDescription. Every loop binding and {currentEntry.*} token then has to be written against whichever convention that particular field happened to land in, and the failure mode is silent: an unresolved token renders as an empty node with no warning. Squashed multi-word IDs like aufstartseite are also easy to mistype, which produces the same silent blank.

Steps to reproduce

  1. Data workspace → New table, kind data.
  2. Add field → type Text, Label Firma URL.
  3. Clear the auto-derived ID and type firmaUrl.
  4. The inline error appears and Create stays disabled: "Must start with a lowercase letter; use letters, numbers, underscores only."
  5. Now create the same field through the API instead — PATCH /admin/api/cms/data/tables/{id} with fields: [{ id: 'firmaUrl', type: 'url', label: 'Firma URL' }] — it is accepted and stored, and the field works normally in loops and {currentEntry.firmaUrl}.
  6. On any post type, note that featuredMedia / seoTitle / seoDescription already exist as camelCase IDs in the same table.

Expected behavior

One convention, applied consistently. Either:

  • the dialog accepts camelCase — matching DataFieldSchema, the API path, and the built-in field IDs; or
  • the restriction is genuinely intended, in which case it should hold at the schema/API layer too, the built-ins should be renamed or documented as grandfathered exceptions, and the dialog's helper text should say why rather than only what.

Either way, an author should not be able to end up with aufstartseite and seoDescription in the same table.

Actual behavior

The lowercase rule is enforced only in the New field dialog. The API path accepts camelCase, the storage schema accepts any string, and the product's own built-in post-type fields are camelCase — so the dialog forbids exactly the convention Instatic uses itself.

Version or commit

main @ a0b1e4e5

Deployment mode

Railway


Found while migrating a 10-page site onto Instatic (4 custom tables, ~30 fields, 63 rows). Happy to send a PR for whichever direction you want — relaxing FIELD_ID_PATTERN is a one-line change, but I'd rather not guess whether the lowercase rule is load-bearing somewhere I haven't looked.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions