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
- Data workspace → New table, kind
data.
- Add field → type
Text, Label Firma URL.
- Clear the auto-derived ID and type
firmaUrl.
- The inline error appears and Create stays disabled: "Must start with a lowercase letter; use letters, numbers, underscores only."
- 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}.
- 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
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:fieldIdErrorgatescanCreateinNewFieldDialog.tsx, so a camelCase ID blocks creation outright rather than being normalized.Meanwhile, in
src/core/data/schemas.ts:…and the storage schema itself has no opinion —
FieldCommonPropsdeclaresid: Type.String(), andnormalizeDataTableFieldsonly filters againstDataFieldSchema. The restriction lives in the dialog and nowhere else.Why it matters in practice. Migrating a real site, I planned
firmaUrl,interviewUrl,aufStartseiteand gotfirmaurl,interviewurl,aufstartseite— while the same records' built-ins stayedfeaturedMediaandseoDescription. 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 likeaufstartseiteare also easy to mistype, which produces the same silent blank.Steps to reproduce
data.Text, LabelFirma URL.firmaUrl.PATCH /admin/api/cms/data/tables/{id}withfields: [{ id: 'firmaUrl', type: 'url', label: 'Firma URL' }]— it is accepted and stored, and the field works normally in loops and{currentEntry.firmaUrl}.featuredMedia/seoTitle/seoDescriptionalready exist as camelCase IDs in the same table.Expected behavior
One convention, applied consistently. Either:
DataFieldSchema, the API path, and the built-in field IDs; orEither way, an author should not be able to end up with
aufstartseiteandseoDescriptionin 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@a0b1e4e5Deployment 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_PATTERNis 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