[{"data":1,"prerenderedAt":264},["ShallowReactive",2],{"sanity-LevEgKvCUg3Ul3GKyhTsfZIfaGswLQ-qsmdjo9-lWe4":3,"sanity-yHNQpfNFvfG7raYsI0m9UchqEhjPV3SaV09voVfnLmI":42,"shiki-block-v-0-0-0-0-0-2-0-1":263},{"data":4},{"_createdAt":5,"_id":6,"_rev":7,"_type":6,"_updatedAt":8,"acceptAllLink":9,"acceptNecessaryLink":18,"bannerColorSource":23,"bannerDescription":24,"bannerTitle":25,"categories":26,"declineAllLink":27,"managePreferencesLink":31,"modalColorSource":23,"modalTitle":35,"preferencesIconColorSource":23,"provider":36,"savePreferencesLink":37,"thirdPartyEmbedCode":26,"title":41},"2026-08-29T18:46:18Z","cookieSettings","C1Syut0iGaGX58H2hZvChS","2026-08-29T18:46:23Z",{"_type":10,"style":11,"title":16,"type":17},"link",{"_type":12,"stylePreset":13},"link.style",{"_ref":14,"_type":15},"link-style-primary","reference","Accept all","button",{"_type":10,"style":19,"title":22,"type":17},{"_type":12,"stylePreset":20},{"_ref":21,"_type":15},"link-style-tertiary-outline","Accept necessary","inherit","We use cookies to enhance your browsing experience and analyse our traffic. Please choose your cookie preferences below.","We use cookies",null,{"_type":10,"style":28,"title":30,"type":17},{"_type":12,"stylePreset":29},{"_ref":21,"_type":15},"Decline all",{"_type":10,"style":32,"title":34,"type":17},{"_type":12,"stylePreset":33},{"_ref":21,"_type":15},"Manage preferences","Cookie Preferences","custom",{"_type":10,"style":38,"title":40,"type":17},{"_type":12,"stylePreset":39},{"_ref":14,"_type":15},"Save preferences","Cookie Settings",{"data":43,"sourceMap":-1},[44],{"_createdAt":5,"_id":45,"_rev":7,"_type":46,"_updatedAt":8,"authorProfile":47,"authorRef":59,"body":61,"categories":175,"excerpt":186,"footer":26,"layout":187,"mainImage":191,"navigation":26,"publishedAt":195,"readTime":196,"relatedPosts":197,"routeOptions":248,"seo":253,"showOnWebsite":133,"slug":252,"tags":259,"title":262},"post-references-not-strings","post",{"avatar":48,"bio":53,"name":54,"role":55,"sameAs":56},{"_type":49,"alt":50,"asset":51},"image","Portrait of Avery Lin",{"_ref":52,"_type":15},"image-7d9d32f0d8cbaa44274ac31144c4acd3b6256c53-512x512-jpg","Avery designs content models for teams moving off page-builder CMSes, and writes about keeping structured content structured.","Avery Lin","Content Architect",[57,58],"https:\u002F\u002Fexample.com\u002Favery-lin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fexample-avery-lin",{"_ref":60,"_type":15},"author-avery-lin",[62,73,82,99,107,115,135,143,159,167],{"_key":63,"_type":64,"children":65,"markDefs":71,"style":72},"b0004","block",[66],{"_key":67,"_type":68,"marks":69,"text":70},"s0005","span",[],"Nothing in a content model looks more reasonable at the time than a plain string. The author is a name, the category is a word, the product tier is one of three things everybody already knows. Typing it is faster than building a document type for it, and the first twenty entries agree with you.",[],"normal",{"_key":74,"_type":64,"children":75,"markDefs":80,"style":81},"b0006",[76],{"_key":77,"_type":68,"marks":78,"text":79},"s0007",[],"Duplication is a promise you cannot keep",[],"h2",{"_key":83,"_type":64,"children":84,"markDefs":98,"style":72},"b0011",[85,89,94],{"_key":86,"_type":68,"marks":87,"text":88},"s0008",[],"The moment a value appears in two documents it has two ",{"_key":90,"_type":68,"marks":91,"text":93},"s0009",[92],"em","spellings",{"_key":95,"_type":68,"marks":96,"text":97},"s0010",[],", and only one of them is right. There is no query that repairs this, because there is nothing structural to repair — the dataset holds exactly what was typed.",[],{"_key":100,"_type":64,"children":101,"markDefs":106,"style":72},"b0012",[102],{"_key":103,"_type":68,"marks":104,"text":105},"s0013",[],"A reference is the same value stored once and pointed at. Rename it, give it a bio, add an avatar, and every post that points at it updates with no migration at all.",[],{"_key":108,"_type":64,"children":109,"markDefs":114,"style":81},"b0014",[110],{"_key":111,"_type":68,"marks":112,"text":113},"s0015",[],"The projection is where it pays off",[],{"_key":116,"_type":64,"children":117,"markDefs":131,"style":72},"b0019",[118,122,127],{"_key":119,"_type":68,"marks":120,"text":121},"s0016",[],"A reference costs an editor one extra click and costs the query one arrow. This is a whole author profile, resolved in the projection so the component receives it as a prop and fetches nothing after mount (",{"_key":123,"_type":68,"marks":124,"text":126},"s0017",[125],"ann0003","GROQ reference documentation",{"_key":128,"_type":68,"marks":129,"text":130},"s0018",[],"):",[132],{"_key":125,"_type":10,"blank":133,"href":134},true,"https:\u002F\u002Fwww.sanity.io\u002Fdocs\u002Fgroq",{"_key":136,"_type":137,"colorSource":23,"markDefs":26,"snippet":138},"code0020","code.block",{"_type":139,"code":140,"filename":141,"language":142},"code","const BLOG_POST_CARD = `\n    _id,\n    title,\n    excerpt,\n    \"slug\": routeOptions.slug.current,\n    publishedAt,\n    \"authorProfile\": authorRef->{ name, role, avatar },\n    \"categories\": categories[]->{ title, \"slug\": slug.current }\n`","shared\u002Fgroq-queries.ts","typescript",{"_key":144,"_type":64,"children":145,"markDefs":158,"style":72},"b0024",[146,150,154],{"_key":147,"_type":68,"marks":148,"text":149},"s0021",[],"The ",{"_key":151,"_type":68,"marks":152,"text":153},"s0022",[139],"->",{"_key":155,"_type":68,"marks":156,"text":157},"s0023",[]," is the entire cost. What you buy with it is that the author is one document, the category list is one set of documents, and neither can drift from itself.",[],{"_key":160,"_type":64,"children":161,"markDefs":166,"style":81},"b0025",[162],{"_key":163,"_type":68,"marks":164,"text":165},"s0026",[],"When a string really is a string",[],{"_key":168,"_type":64,"children":169,"markDefs":174,"style":72},"b0027",[170],{"_key":171,"_type":68,"marks":172,"text":173},"s0028",[],"Tags are the honest exception. They are folksonomy — cheap, disposable, allowed to be messy, and never the thing a page is looked up by. If a value needs a page of its own, a description, or a colour, it stopped being a tag some time ago.",[],[176,182],{"_createdAt":5,"_id":177,"_rev":178,"_type":179,"_updatedAt":5,"slug":180,"title":181},"category-content-modelling","2d20vp0kUSy7uZjqDieLmb","category","content-modelling","Content Modelling",{"_createdAt":5,"_id":183,"_rev":178,"_type":179,"_updatedAt":5,"slug":184,"title":185},"category-developer-experience","developer-experience","Developer Experience","A string is cheap to add and expensive to keep. Every duplicated value is a migration waiting for the day someone spells it differently.",{"_type":188,"layout":189,"pageDesign":190},"post.layout.options","default","design1",{"_type":49,"alt":192,"asset":193},"Abstract teal gradient suggesting linked nodes",{"_ref":194,"_type":15},"image-e64e53386be102ea99eb285dfdd86bfddbc05fb8-1600x900-jpg","2025-11-06T09:00:00.000Z",1,[198,214,232],{"_id":199,"authorProfile":200,"categories":203,"excerpt":206,"mainImage":207,"publishedAt":211,"readTime":196,"slug":212,"title":213},"post-generated-types",{"avatar":201,"name":54,"role":55},{"_type":49,"alt":50,"asset":202},{"_ref":52,"_type":15},[204,205],{"slug":184,"title":185},{"slug":180,"title":181},"Types written by hand describe what you believed the schema said. Types generated from it describe what the schema actually says.",{"_type":49,"alt":208,"asset":209},"Abstract cyan gradient suggesting a type graph",{"_ref":210,"_type":15},"image-17a06078263667a676c81409a6913d585588058a-1600x900-jpg","2026-08-12T09:00:00.000Z","generated-types-are-a-contract","Generated types are a contract, not a convenience",{"_id":215,"authorProfile":216,"categories":219,"excerpt":224,"mainImage":225,"publishedAt":229,"readTime":196,"slug":230,"title":231},"post-design-tokens",{"avatar":217,"name":54,"role":55},{"_type":49,"alt":50,"asset":218},{"_ref":52,"_type":15},[220,223],{"slug":221,"title":222},"design-systems","Design Systems",{"slug":180,"title":181},"When an editor picks a brand colour, half a dozen derived values have to stay legible. Here is how to derive them once instead of hard-coding them everywhere.",{"_type":49,"alt":226,"asset":227},"Abstract magenta gradient representing a derived colour palette",{"_ref":228,"_type":15},"image-c96efd6b55a465dcb615ad80af90714207b25dbc-1600x900-jpg","2026-06-21T09:00:00.000Z","theme-colours-belong-to-the-content","Theme colours belong to the content, not the stylesheet",{"_id":233,"authorProfile":234,"categories":237,"excerpt":239,"mainImage":240,"publishedAt":244,"readTime":245,"slug":246,"title":247},"post-structured-content",{"avatar":235,"name":54,"role":55},{"_type":49,"alt":50,"asset":236},{"_ref":52,"_type":15},[238],{"slug":180,"title":181},"Page-shaped content models feel productive for about six weeks. Here is what to model instead, and how to tell when a field is describing meaning rather than layout.",{"_type":49,"alt":241,"asset":242},"Abstract blue gradient representing structured content layers",{"_ref":243,"_type":15},"image-dd16abed87c4fcb58e0c410eb2451fe86bd707e2-1600x900-jpg","2026-05-14T09:00:00.000Z",2,"structured-content-over-page-builders","Why your CMS should not know what a hero section is",{"_type":249,"includeInSitemap":133,"slug":250},"page.routing.options",{"_type":251,"current":252},"slug","the-reference-you-did-not-make",{"_type":254,"description":255,"favicon":256,"image":26,"socialImages":257,"title":258},"seo","Why a reference is worth the extra click: one value, one spelling, and no migration when it changes.",{"mimeType":26,"url":26},{"facebook":26,"linkedin":26,"twitter":26},"References beat duplicated strings in a content model",[181,260,261],"References","GROQ","The reference you did not make is the migration you will","\u003Cpre class=\"shiki shiki-themes github-light github-dark nuxion-code__pre\" style=\"--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#22863A;--shiki-dark:#85E89D\">script\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> setup\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> lang\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#032F62;--shiki-dark:#9ECBFF\">\"ts\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F Fetched once, at build time, into the prerendered payload.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> { \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\">data\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> } \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> await\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> useSanityContent\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">BlogIndexQueryResult\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">>(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    blogIndexQuery\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F Paging and search are derived from it — no second request.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#005CC5;--shiki-dark:#79B8FF\"> matching\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\"> computed\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(() \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">=>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">    (data.value?.posts \u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#D73A49;--shiki-dark:#F97583\">??\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\"> []).\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#6F42C1;--shiki-dark:#B392F0\">filter\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">(matchesFilters)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u002F\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#22863A;--shiki-dark:#85E89D\">script\u003C\u002Fspan>\u003Cspan style=\"--shiki-light:#24292E;--shiki-dark:#E1E4E8\">>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>",1788029822935]