Project structure
Where everything lives in the two apps.
apps/├─ studio/ Sanity Studio└─ web/ Nuxt sitedocs/ these guides, imported into the Docs collectionStudio (apps/studio)
schemaTypes/├─ documents/ page, collection, entry, author, settings├─ objects/ link, seo, blockContent, table, pageBuilder, codeGroup, featureItem├─ blocks/ one file per page-builder block└─ shared/ field helpers (icon, image, orientation, code, languages)presentation/ where each document appears on the site (Presentation tool)scripts/├─ seed.ts example content├─ docs.ts Markdown guides → Docs collection└─ lib/ Markdown → Portable Text converter, docs importerstructure.ts the Studio menusanity.config.tsSite (apps/web)
app/├─ pages/[...slug].vue resolves every URL: page, collection or entry├─ app.vue header, footer, languages, site-wide SEO├─ components/│ ├─ blocks/ Block*.vue, one per block│ ├─ PageBlocks.vue block registry│ ├─ CollectionView.vue collection list page│ ├─ EntryList.vue blog, cards, gallery and docs layouts│ ├─ EntryView.vue entry page (docs sidebar, TOC, related entries)│ ├─ PortableText.vue rich text renderer│ ├─ CodeBlock.vue code blocks with Shiki│ ├─ LanguageSwitcher.vue Nuxt UI LocaleSelect│ ├─ SiteSearch.vue search palette│ └─ OgImage/ generated social image├─ composables/ usePageSeo, useSanityImage, useSiteLanguage├─ utils/ queries.ts (GROQ), language, labels, links, toc└─ assets/css/main.css theme and motion utilitiesmodules/ui-locales.ts lazy Nuxt UI localesserver/├─ api/highlight.post.ts syntax highlighting├─ api/__sitemap__/urls.ts sitemap entries from Sanity└─ preview/ preview routes that skip the Vercel cacheprerender-routes.ts every URL, for hybrid and static buildssanity.types.ts generated by pnpm typegennuxt.config.tsHow a request is served
- Every URL hits
app/pages/[...slug].vue. - The language prefix, if any, is taken off the path (
/id/blog/post→ languageid, pathblog/post). ROUTE_QUERYlooks for a page, a collection and an entry matching the path in one request.- The page renders
PageBlocks,CollectionVieworEntryView; a miss is a 404.
Header, footer and settings come from SETTINGS_QUERY in app.vue, which runs before the page.