Recognize Webflow's content modeling patterns
Webflow's CMS has structural constraints that shape how teams model content. Carrying those patterns directly into Sanity produces a schema that repeats the same limitations. Before designing your Sanity schema, check your collections against these eight patterns.
A collection with no standalone page that exists only to be referenced by another collection is almost always an inline array of objects in Sanity, not a document type.
Ask two questions for each collection:
- Does this collection have its own page in the site?
- Would any of its items ever appear standalone, without the parent?
If both answers are no, it belongs as an array field on the parent document.
Example: a FAQ Items collection that only appears on a FAQ Page becomes an inline array of FAQ objects on the page document.
step_1_title, step_2_title, step_3_title is an array with nowhere to go. In Sanity, that becomes a typed array field with one schema definition.
feature_a | feature_b | feature_c in a plain text field is also an array pattern. These become proper array fields in Sanity.
Content that should be structured (author bios, product specs, callout blocks) often ends up in Webflow rich text because there's no better place for it. In Sanity, Portable Text supports custom block types that hold fully structured, typed content alongside prose.
Webflow has no computed fields and no query-time transforms, so editors end up storing every display variant as its own field. A tag document might have name: "td", nameWithBrackets: "<td>", actualName: "Table Data Cell", and navText: "td": four fields where only one is actually source data. The others are formatting applied at the field level because Webflow's templates can't compute them.
In Sanity, ask for each field: can this be derived from another field I already have? If yes, don't store it, derive it:
"displayTitle": "<" + name + "> Tag"The only variant worth storing is one that genuinely varies per document in ways the derivation formula can't capture — an editorial override, not a formatting rule.
Webflow has no conditional field logic, so teams create isFeatured, isHidden, isNew, isPopular as separate boolean fields where a single status string with an options list would cover it. The signal is multiple is* fields that are mutually exclusive or represent a single editorial decision. Collapse these into one field:
defineField({ name: 'status', type: 'string', options: { list: ['featured', 'new', 'hidden'] },})Keep a boolean only when the state is genuinely independent of everything else.
Webflow doesn't have enums or inline option lists, so teams create a Category collection with five items and reference it from everything — not because categories need their own pages, but because it was the only way to constrain the value to a fixed set. Check whether the referenced collection has any fields beyond name and slug, and whether its items have their own pages. If not, it may be better modeled as a string field with options.list. Keep it as a reference only if the target documents have real content and a public URL.
Webflow templates can't construct URLs dynamically, so teams sometimes store the full path (/tags/td) as a field alongside the slug (td): one for linking, one for the slug input. In Sanity you derive the URL from the slug at render time. The stored path field is redundant and drifts out of sync when slugs change. Drop it and reconstruct the URL in your GROQ projection or component instead:
"path": "/tags/" + slug.currentWork through your audit document and flag every collection and field that matches one of these patterns. Those flags are your schema design starting points.
Next, you’ll design your Sanity schemas from the audit findings.