├── .eslintrc.json ├── .gitignore ├── .gitpod.yml ├── .nvmrc ├── .prettierrc ├── .vscode └── settings.json ├── .yarn ├── patches │ └── @stackblitz-sdk-npm-1.7.0-alpha.3-1f8676ad43.patch ├── plugins │ └── @yarnpkg │ │ ├── plugin-interactive-tools.cjs │ │ └── plugin-typescript.cjs └── releases │ └── yarn-3.2.0.cjs ├── .yarnrc.yml ├── LICENSE ├── README.md ├── assets └── contentlayer-generated │ ├── index.d.ts │ └── types.d.ts ├── content ├── blog │ ├── beta.mdx │ └── working-with-content-is-hard-for-developers.mdx ├── config │ └── global.yaml ├── docs │ ├── 100-getting-started │ │ └── index.mdx │ ├── 200-concepts │ │ ├── 100-how-contentlayer-works.mdx │ │ ├── 200-content-modeling.mdx │ │ ├── 300-type-safety.mdx │ │ ├── 400-comparison.mdx │ │ └── index.mdx │ ├── 300-sources │ │ ├── 100-files │ │ │ ├── 100-mapping-document-types.mdx │ │ │ ├── 200-generated-data.mdx │ │ │ ├── 300-generated-types.mdx │ │ │ ├── 400-mdx.mdx │ │ │ ├── 500-images.mdx │ │ │ └── index.mdx │ │ ├── 125-notion │ │ │ ├── 100-getting-started.mdx │ │ │ ├── 200-configure-databases.mdx │ │ │ ├── 300-configure-properties.mdx │ │ │ ├── 400-configure-renderer.mdx │ │ │ ├── 500-images.mdx │ │ │ └── index.mdx │ │ ├── 150-remote-files.mdx │ │ ├── 200-contentful.mdx │ │ ├── 300-sanity.mdx │ │ └── index.mdx │ ├── 400-environments │ │ ├── 100-nextjs.mdx │ │ ├── 200-remix.mdx │ │ ├── 300-svelte.mdx │ │ ├── 400-astro.mdx │ │ ├── 500-vite.mdx │ │ └── index.mdx │ ├── 500-reference │ │ ├── 100-cli.mdx │ │ ├── 200-next-contentlayer.mdx │ │ ├── 200-source-files │ │ │ ├── 100-make-source.mdx │ │ │ ├── 200-define-document-type.mdx │ │ │ ├── 300-define-nested-type.mdx │ │ │ ├── 400-field-types.mdx │ │ │ └── index.mdx │ │ ├── 250-source-notion │ │ │ ├── 100-make-source.mdx │ │ │ ├── 200-define-database.mdx │ │ │ ├── 300-properties.mdx │ │ │ └── index.mdx │ │ └── index.mdx │ ├── 600-integrations │ │ ├── 100-stackbit │ │ │ ├── 100-tutorial.mdx │ │ │ ├── 200-config.mdx │ │ │ ├── 300-dev-server.mdx │ │ │ └── index.mdx │ │ └── index.mdx │ ├── 700-other │ │ ├── 100-faq.mdx │ │ ├── 200-roadmap.mdx │ │ ├── 300-changelog.mdx │ │ ├── 400-contributing.mdx │ │ ├── 500-known-problems.mdx │ │ └── index.mdx │ └── index.mdx └── examples │ ├── 100-nextjs.mdx │ ├── 200-vite.mdx │ ├── 900-other.mdx │ └── index.mdx ├── contentlayer.config.ts ├── netlify.toml ├── next-env.d.ts ├── next-sitemap.js ├── next.config.js ├── package.json ├── postcss.config.js ├── public ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-167x167.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── favicon-128x128.png │ ├── favicon-16x16.png │ ├── favicon-196x196.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ └── mstile-70x70.png ├── fonts │ └── virgil.woff2 └── images │ ├── beta-launch-post-meta.png │ ├── content-as-data.png │ ├── content-is-hard-meta.png │ ├── content-timeline.png │ ├── contentlayer-in-five-minutes.png │ ├── intro-thumbnail.jpg │ ├── local-data-transformation.png │ ├── logos │ ├── astro.svg │ ├── contentful.svg │ ├── mdx.svg │ ├── nextjs.svg │ ├── notion.svg │ ├── remix.svg │ ├── sanity.svg │ └── vite.svg │ ├── notion-contentlayer-source.png │ ├── notion │ ├── database.png │ └── integration_granular_permissions.gif │ ├── performance-comparison.png │ ├── playground-hint-dev-server.png │ ├── playground-hint-edit.png │ ├── playground-hint-updates.png │ ├── post-feed.png │ ├── post-layout.png │ └── stackbit-visual-editing.gif ├── scripts └── generate-page-ids.mjs ├── src ├── components │ ├── ColorSchemeContext.tsx │ ├── SearchContext.tsx │ ├── blog │ │ ├── BenchmarkResults.tsx │ │ ├── BlogDetails.tsx │ │ ├── BlogHeader.tsx │ │ ├── BlogPreview.tsx │ │ ├── BulletList.tsx │ │ ├── ContentStack.tsx │ │ ├── Playground.tsx │ │ ├── RelatedPosts.tsx │ │ └── TLDR.tsx │ ├── common │ │ ├── Arrow │ │ │ ├── CurvedLong.tsx │ │ │ ├── CurvedShort.tsx │ │ │ ├── LoopedLong.tsx │ │ │ ├── LoopedShort.tsx │ │ │ ├── StraightDashed.tsx │ │ │ ├── StraightLong.tsx │ │ │ ├── StraightShort.tsx │ │ │ └── index.tsx │ │ ├── Author.tsx │ │ ├── Button.tsx │ │ ├── Callout.tsx │ │ ├── Card.tsx │ │ ├── ChevronLink.tsx │ │ ├── ColorSchemeSwitcher.tsx │ │ ├── Container.tsx │ │ ├── Footer.tsx │ │ ├── Headings.tsx │ │ ├── Icon │ │ │ ├── API.tsx │ │ │ ├── Bars.tsx │ │ │ ├── BrokenLink.tsx │ │ │ ├── Calendar.tsx │ │ │ ├── Check.tsx │ │ │ ├── CheckCircle.tsx │ │ │ ├── CheckCircleOutline.tsx │ │ │ ├── ChevronDown.tsx │ │ │ ├── ChevronLeft.tsx │ │ │ ├── ChevronRight.tsx │ │ │ ├── Close.tsx │ │ │ ├── Code.tsx │ │ │ ├── CodeLight.tsx │ │ │ ├── Collapse.tsx │ │ │ ├── Contentful.tsx │ │ │ ├── Contentlayer.tsx │ │ │ ├── CrossCircleOutline.tsx │ │ │ ├── Database.tsx │ │ │ ├── Discord.tsx │ │ │ ├── Exclamation.tsx │ │ │ ├── Expand.tsx │ │ │ ├── ExternalLink.tsx │ │ │ ├── Gear.tsx │ │ │ ├── GitHub.tsx │ │ │ ├── Gitpod.tsx │ │ │ ├── GraphQL.tsx │ │ │ ├── Info.tsx │ │ │ ├── Jekyll.tsx │ │ │ ├── Lightning.tsx │ │ │ ├── Markdown.tsx │ │ │ ├── Moon.tsx │ │ │ ├── Notion.tsx │ │ │ ├── PHP.tsx │ │ │ ├── PlayButton.tsx │ │ │ ├── Plus.tsx │ │ │ ├── Question.tsx │ │ │ ├── React.tsx │ │ │ ├── Rocket.tsx │ │ │ ├── Search.tsx │ │ │ ├── Sign.tsx │ │ │ ├── Sun.tsx │ │ │ ├── Template.tsx │ │ │ ├── Users.tsx │ │ │ ├── WordPress.tsx │ │ │ └── index.tsx │ │ ├── Label.tsx │ │ ├── Link.tsx │ │ ├── Logo.tsx │ │ ├── MainNavigation.tsx │ │ ├── PageNavigation.tsx │ │ └── User.tsx │ ├── docs │ │ ├── DocsCard.tsx │ │ ├── DocsFooter.tsx │ │ ├── DocsHeader.tsx │ │ ├── DocsNavigation.tsx │ │ └── OptionsTable.tsx │ ├── examples │ │ └── ExamplesFooter.tsx │ └── landing-page │ │ ├── Checklist.tsx │ │ ├── CodeWindow.tsx │ │ ├── Dashed.tsx │ │ ├── DataTransformation.tsx │ │ ├── FAQ.tsx │ │ ├── Features.tsx │ │ ├── FileTree.tsx │ │ ├── Heading.tsx │ │ ├── Hero.tsx │ │ ├── HowItWorks.tsx │ │ ├── Paragraph.tsx │ │ ├── Playground.tsx │ │ ├── Support.tsx │ │ ├── Testimonials.tsx │ │ ├── Tweets.tsx │ │ └── Video.tsx ├── contentlayer │ ├── document │ │ ├── Doc.ts │ │ ├── Example.ts │ │ ├── GlobalConfig.ts │ │ └── Post.ts │ ├── index.ts │ ├── nested │ │ ├── Link.ts │ │ └── SEO.ts │ └── utils.ts ├── pages │ ├── 404.tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── blog │ │ ├── [slug].tsx │ │ └── index.tsx │ ├── docs │ │ └── [[...slug]].tsx │ ├── examples │ │ └── [[...slug]].tsx │ └── index.tsx ├── styles │ ├── globals.css │ ├── hljs-github-dark.css │ ├── markdown.css │ ├── tailwind.css │ └── twoslash-shiki.css └── utils │ ├── blog │ └── beta-post-snippets.ts │ ├── build-docs-tree.ts │ ├── build-examples-tree.ts │ ├── helpers.ts │ ├── next.ts │ ├── object.ts │ ├── sluggify.ts │ ├── syntax-highlighting.ts │ ├── used-by-count.ts │ └── validate-duplicate-ids.ts ├── tailwind.config.js ├── tsconfig.json ├── types ├── PathSegment.d.ts └── TreeNode.d.ts ├── vercel.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:import/recommended", "next/core-web-vitals"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.yarn/* 2 | !/.yarn/releases 3 | !/.yarn/plugins 4 | !/.yarn/sdks 5 | !/.yarn/patches 6 | 7 | # dependencies 8 | /node_modules 9 | /.pnp 10 | .pnp.js 11 | 12 | # testing 13 | /coverage 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | tmp 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env.local 34 | .env.development.local 35 | .env.test.local 36 | .env.production.local 37 | 38 | # vercel 39 | .vercel 40 | 41 | # contentlayer 42 | .contentlayer 43 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: npm install 3 | command: npm run dev 4 | ports: 5 | - port: 3000 6 | onOpen: open-preview 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "trailingComma": "all", 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.codeActionsOnSave": { 4 | "source.organizeImports": "explicit", 5 | "source.fixAll": "explicit", 6 | "source.addMissingImports": "explicit" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs 5 | spec: '@yarnpkg/plugin-typescript' 6 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 7 | spec: '@yarnpkg/plugin-interactive-tools' 8 | 9 | yarnPath: .yarn/releases/yarn-3.2.0.cjs 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Stackbit Inc., Johannes Schickling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contentlayer Website 2 | 3 | [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/contentlayerdev/website) 4 | 5 | ## Local setup 6 | 7 | ```bash 8 | npm install 9 | npm run dev 10 | ``` 11 | 12 | Open [https://localhost:3000](https://localhost:3000) with your browser to see the result. (Note the webserver is using a self-signed SSL certificate since HTTPS is required for the embedded Stackblitz editor to work properly.) 13 | 14 | ## Live preview 15 | 16 | [Current Vercel Deployment](https://website-git-new-landing-page-schick.vercel.app) 17 | 18 | ## Generating Global Doc ID 19 | 20 | Every document (except docs index page) has a unique eight-character `global_id` property. This uniquely identifies that piece of documentation and provides a seamless way to be able to reorganize documentation without worrying about 404 errors for missing redirects. 21 | 22 | These can be automatically generated by running the following command: 23 | 24 | node scripts/generate-page-ids.mjs 25 | 26 | Validate that all `global_id` values are unique with this command: 27 | 28 | node scripts/validate-duplicate-ids.mjs 29 | -------------------------------------------------------------------------------- /assets/contentlayer-generated/index.d.ts: -------------------------------------------------------------------------------- 1 | // NOTE This file is auto-generated by the Contentlayer CLI 2 | 3 | import { Post, DocumentTypes } from './types' 4 | 5 | export type * from './types' 6 | 7 | export declare const allPosts: Post[] 8 | 9 | export declare const allDocuments: DocumentTypes[] 10 | -------------------------------------------------------------------------------- /assets/contentlayer-generated/types.d.ts: -------------------------------------------------------------------------------- 1 | // NOTE This file is auto-generated by the Contentlayer CLI 2 | 3 | import type { Markdown, MDX } from 'contentlayer/core' 4 | import * as Local from 'contentlayer/source-files' 5 | 6 | export { isType } from 'contentlayer/client' 7 | 8 | // export type Image = string 9 | export type { Markdown, MDX } 10 | 11 | /** Document types */ 12 | 13 | export type Post = { 14 | /** File path relative to `contentDirPath` */ 15 | _id: string 16 | _raw: Local.RawDocumentData 17 | type: 'Post' 18 | /** The title of the page */ 19 | title: string 20 | /** Markdown file body */ 21 | body: Markdown 22 | /** The URL path of this page relative to site root. For example, the site root page would be "/", and doc page would be "docs/getting-started/" */ 23 | url: string 24 | } 25 | 26 | /** Nested types */ 27 | 28 | /** Helper types */ 29 | 30 | export type AllTypes = DocumentTypes | NestedTypes 31 | export type AllTypeNames = DocumentTypeNames | NestedTypeNames 32 | 33 | export type DocumentTypes = Post 34 | export type DocumentTypeNames = 'Post' 35 | 36 | export type NestedTypes = never 37 | export type NestedTypeNames = never 38 | 39 | export interface ContentlayerGenTypes { 40 | documentTypes: DocumentTypes 41 | documentTypeMap: DocumentTypeMap 42 | documentTypeNames: DocumentTypeNames 43 | nestedTypes: NestedTypes 44 | nestedTypeMap: NestedTypeMap 45 | nestedTypeNames: NestedTypeNames 46 | allTypeNames: AllTypeNames 47 | } 48 | 49 | // declare global { 50 | // interface ContentlayerGen extends ContentlayerGenTypes {} 51 | // } 52 | 53 | export type DocumentTypeMap = { 54 | Post: Post 55 | } 56 | 57 | export type NestedTypeMap = {} 58 | -------------------------------------------------------------------------------- /content/config/global.yaml: -------------------------------------------------------------------------------- 1 | title: Contentlayer 2 | -------------------------------------------------------------------------------- /content/docs/200-concepts/200-content-modeling.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: dc68721f 3 | title: Content Modeling with Contentlayer 4 | nav_title: Content Modeling 5 | excerpt: Why content modeling is necessary and how it differs between local and remote sources. 6 | --- 7 | 8 | Contentlayer's primary job is to transform your content into data that can be imported into the pages of your web project. To do that effectively, Contentlayer has to know the shape of your content — your _content schema_. 9 | 10 | This is handled differently depending on whether you're working with a local source (Files) or a remote source. 11 | 12 | ## Remote Sources 13 | 14 | Remote sources are experimental. Expect this section to change. 15 | 16 | With remote sources — i.e. headless CMS — the content schema is pulled in automatically from the source's API. 17 | 18 | However, we're still experimenting with exactly how remote sources will work. As we continue to explore and add new sources, we'll update this section. 19 | 20 | ## Local Sources 21 | 22 | When using a local source, your schema must be explicitly defined so Contentlayer knows how to handle the local files. 23 | 24 | You can learn more about local sources by [exploring the files source guides](/docs/sources/files). 25 | 26 | Below you'll find a few concepts that are important to understand when modeling content locally. 27 | 28 | ### Documents vs Nested 29 | 30 | There are two types of objects — _documents_ and _nested (documents)_. 31 | 32 | **Documents** are objects generated by Contentlayer, according to a _document type definition_. Think of a document type like a model or a table in a database. A document is then an instance of that model — i.e. the result of a database query. 33 | 34 | Contentlayer writes documents to individual JSON files, which can then be imported into your project's pages. 35 | 36 | **Nested** documents are objects that are embedded directly in a document. They are defined as _nested types_ and are primarily used for repeatable shapes. 37 | 38 | For example, suppose you had an SEO object that you wanted included on various models. You could define the nested document type once, and then use it within any model that requires it. The [`defineNestedType` API reference](/docs/reference/source-files/define-nested-type) has more information and examples. 39 | 40 | ### Content References 41 | 42 | Just like in a database, content can be associated with other content. For example, a post document type can reference tag documents. 43 | 44 | However, [references are not currently transformed or embedded](/docs/reference/source-files/field-types#reference). They are passed through as a file path reference to the related object. When working with references, we recommend building utility functions that will retrieve associated data as needed. 45 | -------------------------------------------------------------------------------- /content/docs/200-concepts/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: ac167d19 3 | title: Concepts 4 | show_child_cards: true 5 | excerpt: Background information on how Contentlayer works. 6 | --- 7 | 8 | This is the starting point for learning what Contentlayer is all about. Why we built Contentlayer, how it works, comparison to other products, and the practices we believe in. 9 | -------------------------------------------------------------------------------- /content/docs/300-sources/100-files/300-generated-types.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: fda1d801 3 | title: Generated Type Definitions 4 | excerpt: Contentlayer automatically generates type definitions for content that lives locally. 5 | --- 6 | 7 | Alongside your [generated documents](/docs/sources/files/generated-data) you'll find a `types` file at `.contentlayer/generated/index.d.ts`. 8 | 9 | This file exports a number of auto-generated and provided types that you can use to ensure type safety throughout your application. 10 | 11 | ## Generated Types 12 | 13 | A type is generated for every document and nested type you've defined in your Contentlayer configuration. 14 | 15 | These types will match the structure of your documents, including both generated and reserved properties. 16 | 17 | Consider an example where you have a `Page` document type that has three fields: `title`, `description`, and `seo`, along with `body` content. And let's say the `seo` field is a nested object of type `SEO`, also with a `title` and `description`. 18 | 19 | The exported types will look something like this: 20 | 21 | ```ts 22 | export type SEO = { 23 | type: 'SEO' 24 | title: string 25 | description: string | undefined 26 | } 27 | 28 | export type Page = { 29 | _id: string 30 | _raw: Local.RawDocumentData 31 | type: 'Page' 32 | title: string 33 | description: string | undefined 34 | seo: SEO | undefined 35 | body: Markdown 36 | } 37 | ``` 38 | 39 | ## Provided Types 40 | 41 | This file also exports a number of provided types that may come in handy in your project. Though there are more than you could inspect, here are those that may be of most use to you: 42 | 43 | - `Markdown`: Object with properties `raw` and `html`. 44 | - `MDX`: Object with properties `raw` and `code`. 45 | - `AllTypes`: Accepts any document type or nested type. 46 | - `DocumentTypes`: Accepts any document type. 47 | - `NestedTypes`: Accepts any nested type. 48 | -------------------------------------------------------------------------------- /content/docs/300-sources/100-files/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: ae74398f 3 | title: Files Source 4 | nav_title: Files 5 | collapsible: true 6 | collapsed: true 7 | excerpt: Guides for working with Contentlayer when using local files as your content source. 8 | --- 9 | 10 | Using a files source with Contentlayer means that content lives locally, right alongside your code in your web project repository. These is most often markdown or MDX files, but can also be content in the JSON or YAML syntax. 11 | 12 |
13 | 19 |
20 |

Our getting started tutorial is the best place to start when wanting to work with local content.

21 |

It shows you how to work with local markdown files within a Next.js application.

22 |
23 |
24 | 25 | --- 26 | 27 | ## Local Source Guides 28 | 29 |
30 |
Local source guides will teach you the crucial aspects of working with local content.
31 |
32 | - 33 | - 34 | - 35 | - 36 | - 37 |
38 |
39 | 40 | --- 41 | 42 | ## API Reference 43 | 44 |
45 |
46 | Working with local content uses the `contentlayer/local-source` module. 47 | 48 |
49 |
50 | - 51 | - 52 | - 53 | - 54 |
55 |
56 | -------------------------------------------------------------------------------- /content/docs/300-sources/125-notion/500-images.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: e3a04016 3 | title: Images 4 | nav_title: Images 5 | excerpt: Options on image processing when using Notion as your content source. 6 | --- 7 | 8 |
9 | ⚠ The content source plugin `contentlayer-source-notion` is currently in Alpha and should not be used in production. 10 |
11 | 12 | --- 13 | 14 | When querying images from Notion it give a temporary link that expires after a certain amount of time. 15 | 16 | Contentlayer does not currently support image processing, though we're [planning on implementing it](https://github.com/contentlayerdev/contentlayer/issues/11). 17 | Until then, there are a few different approaches you can take to working with images when your content is processed by Contentlayer: 18 | 19 | ## Using an Image Management Services 20 | 21 | Alternatively, you can use an image management service like [Cloudinary](https://cloudinary.com/) or [Imgix](https://imgix.com/). (There are many to choose from.) 22 | 23 | These services allow you to use URL parameters to determine the size and shape of your image. The image is then optimized by these services and delivered to your users. 24 | 25 | These services deliver an optimized image, but it's still up to you to ensure that image doesn't degrade the performance of your pages. Therefore, you may also want to consider manually processing the images. See below for details. 26 | 27 | ### Processing Images as Computed Fields 28 | 29 | To process images during the Contentlayer build, you can use a computed field. 30 | Say you've a property `thumbnail`. 31 | 32 | Then you'd add a _computed_ field like `image` that would do the processing and returns the path/url to the processed image. 33 | 34 | ```js 35 | defineDatabase(() => ({ 36 | name: 'Post', 37 | computedFields: { 38 | thumbnail: { 39 | type: 'string', 40 | resolve: (doc) => { 41 | // do the processing, and return new value ... 42 | }, 43 | }, 44 | }, 45 | })) 46 | ``` 47 | 48 | ### Processing after the Contentlayer Build 49 | 50 | Another option is to build a utility method within your application that does the processing. In this case, Contentlayer would have already processed the image reference as a string, which you would send to the processing utility before building the props to send to the page. 51 | 52 | ### Handling Images in Markdown with Contentlayer 53 | 54 | It's worth noting that the methods above handle processing images only that were specified in the frontmatter. If you want to process images you've used within the body of a markdown or MDX file, you'll want to build (or find) a remark or rehype plugin. 55 | -------------------------------------------------------------------------------- /content/docs/300-sources/125-notion/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: b2ce5957 3 | title: Notion Source 4 | nav_title: Notion 5 | label: Experimental 6 | collapsible: true 7 | collapsed: true 8 | excerpt: Guides for working with Contentlayer when using Notion as your content source. 9 | --- 10 | 11 |
12 | ⚠ The content source plugin `contentlayer-source-notion` is currently in Alpha and should not be used in production. 13 |
14 | 15 | This plugin allows you to use Notion as a content source. It automatically generates type by inferring your page properties and transform Rich Text into HTML. 16 | 17 |
18 | 23 | Our getting started tutorial shows you how to use Notion to manage your Next.JS Blog. 24 | 25 |
26 |

Follow the getting started tutorial to start working with Notion as a Content source.

27 |
28 |
29 | 30 | --- 31 | 32 | ## Notion Source Guides 33 | 34 |
35 |
Notion source guides will teach you the crucial aspects of working with Notion.
36 |
37 | - 38 | - 39 | - 40 | - 41 | - 42 |
43 |
44 | 45 | --- 46 | 47 | ## API Reference 48 | 49 |
50 |
51 | Working with Notion uses the `contentlayer/notion-source` module. 52 | 53 |
54 |
55 | - 56 | - 57 | - 58 |
59 |
60 | -------------------------------------------------------------------------------- /content/docs/300-sources/150-remote-files.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: fbb47906 3 | title: Remote Files 4 | label: Experimental 5 | excerpt: How to use Contentlayer with remote files as content source (e.g. Git repo) 6 | --- 7 | 8 | The remote files source acts very similarly to the [files source](/docs/sources/files) with the difference that the content files can live outside the website folder. Using the remote files source Contentlayer will automatically **sync the content files from your remote files source to your local website folder** and then process them using the regular files source. 9 | 10 | Common remote file sources including other Git repositories, databases, APIs or anywhere else where your content comes from. 11 | 12 | ## Example: Remote Git Repository 13 | 14 | See [full example here](https://github.com/contentlayerdev/contentlayer/tree/main/examples/node-script-remote-content). 15 | 16 | ```ts 17 | // NOTE we're using the `defineDocumentType` from the regular files source 18 | import { defineDocumentType } from '@contentlayer/source-files' 19 | import { spawn } from 'node:child_process' 20 | import { makeSource } from '@contentlayer/source-remote-files' 21 | 22 | const Post = defineDocumentType(() => ({ 23 | name: 'Post', 24 | filePathPattern: `docs/**/*.md`, 25 | fields: { 26 | title: { type: 'string', required: false }, 27 | }, 28 | })) 29 | 30 | const syncContentFromGit = async (contentDir: string) => { 31 | const syncRun = async () => { 32 | const gitUrl = 'https://github.com/vercel/next.js.git' 33 | // TODO: git pull or git clone (see full example for working code) 34 | } 35 | 36 | let wasCancelled = false 37 | let syncInterval 38 | 39 | const syncLoop = async () => { 40 | await syncRun() 41 | 42 | if (wasCancelled) return 43 | 44 | syncInterval = setTimeout(syncLoop, 1000 * 60) 45 | } 46 | 47 | // Block until the first sync is done 48 | await syncLoop() 49 | 50 | return () => { 51 | wasCancelled = true 52 | clearTimeout(syncInterval) 53 | } 54 | } 55 | 56 | export default makeSource({ 57 | syncFiles: syncContentFromGit, 58 | contentDirPath: 'nextjs-repo', 59 | contentDirInclude: ['docs'], 60 | documentTypes: [Post], 61 | disableImportAliasWarning: true, 62 | }) 63 | ``` 64 | 65 | ## Notes 66 | 67 | - It's recommended to add the synced content directory to your `.gitignore` file 68 | - It's possible to combine the regular files source and the remote files source. You'll have to use the same root `contentDirPath` and put the respective content in different sub directories. 69 | -------------------------------------------------------------------------------- /content/docs/300-sources/200-contentful.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: d72dff47 3 | title: Contentful 4 | label: Planned 5 | excerpt: We are planning to officially support Contentful. Add your vote to help us prioritize the work. 6 | --- 7 | 8 | Support for Contentful is planned, but it is not yet stable. To help us prioritize this work, please add your vote to [this GitHub issue](https://github.com/contentlayerdev/contentlayer/issues/173). 9 | -------------------------------------------------------------------------------- /content/docs/300-sources/300-sanity.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: c4fdd0f7 3 | title: Sanity 4 | label: Considering 5 | excerpt: We're considering officially supporting Sanity. Add your vote. 6 | --- 7 | 8 | We're currently considering support for Sanity. Please add your vote to [this GitHub issue](https://github.com/contentlayerdev/contentlayer/issues/172) to help us prioritize the work. 9 | -------------------------------------------------------------------------------- /content/docs/300-sources/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: acc47cf6 3 | title: Content Sources 4 | nav_title: Sources 5 | excerpt: Solving specific scenarios using Contentlayer. 6 | show_child_cards: true 7 | --- 8 | 9 | Contentlayer lets you choose the source for your content. 10 | 11 | Currently only local file source is officially supported. Visit the others for instructions on how to add your vote. 12 | 13 | [Join our Discord community](https://discord.gg/rytFErsARm) to suggest new sources. 14 | -------------------------------------------------------------------------------- /content/docs/400-environments/200-remix.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: b3975f99 3 | title: Remix 4 | label: Considering 5 | excerpt: We're considering officially supporting Remix. Add your vote. 6 | --- 7 | 8 | We're currently considering support for Remix. Please add your vote to [this GitHub issue](https://github.com/contentlayerdev/contentlayer/issues/169) to help us prioritize the work. 9 | -------------------------------------------------------------------------------- /content/docs/400-environments/300-svelte.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: efe6735d 3 | title: SvelteKit 4 | label: Considering 5 | excerpt: We're considering officially supporting Svelte and SvelteKit. Add your vote. 6 | --- 7 | 8 | We're currently considering support for SvelteKit. Please add your vote to [this GitHub issue](https://github.com/contentlayerdev/contentlayer/issues/170) to help us prioritize the work. 9 | -------------------------------------------------------------------------------- /content/docs/400-environments/400-astro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: d57135c9 3 | title: Astro 4 | label: Considering 5 | excerpt: We're considering officially supporting Astro. Add your vote. 6 | --- 7 | 8 | We're currently considering support for Astro. Please add your vote to [this GitHub issue](https://github.com/contentlayerdev/contentlayer/issues/171) to help us prioritize the work. 9 | -------------------------------------------------------------------------------- /content/docs/400-environments/500-vite.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: ba80b355 3 | title: Vite 4 | label: Considering 5 | excerpt: We're considering officially supporting Vite. Add your vote. 6 | --- 7 | 8 | We're currently considering support for Vite. Please add your vote to [this GitHub issue](https://github.com/contentlayerdev/contentlayer/issues/179) to help us prioritize the work. 9 | -------------------------------------------------------------------------------- /content/docs/400-environments/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: c6df80ab 3 | title: Environments 4 | excerpt: Solving specific scenarios using Contentlayer. 5 | show_child_cards: true 6 | --- 7 | 8 | Contentlayer supports a tight integration with Next.js. 9 | 10 | Other environment support is coming soon. Visit those we're considering below for instructions on how to add your vote. 11 | 12 | [Join our Discord community](https://discord.gg/rytFErsARm) to suggest new environments. 13 | -------------------------------------------------------------------------------- /content/docs/500-reference/100-cli.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: e9e2f788 3 | title: Contentlayer CLI 4 | nav_title: CLI 5 | excerpt: Working with Contentlayer on the command line. 6 | --- 7 | 8 | The Contentlayer CLI enables you to transform your content into data. 9 | 10 | ## Usage 11 | 12 | ```txt 13 | contentlayer [options] 14 | ``` 15 | 16 | To get a list of all commands, you can run `contentlayer` with the `--help` flag: 17 | 18 | ```txt 19 | contentlayer --help 20 | ``` 21 | 22 | Which will produce an output that looks like this: 23 | 24 | ```txt 25 | ━━━ Contentlayer CLI - 0.0.34 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 26 | 27 | $ contentlayer 28 | 29 | ━━━ General commands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 30 | 31 | contentlayer build [-c,--config #0] [--clearCache] [--verbose] 32 | Transforms your content into static data 33 | 34 | contentlayer dev [-c,--config #0] [--clearCache] [--verbose] 35 | Same as "contentlayer build" but with watch mode 36 | 37 | You can also print more details about any of these commands by calling them 38 | with the `-h,--help` flag right after the command name. 39 | ``` 40 | 41 | ## Commands 42 | 43 | ### `build` 44 | 45 | ```txt 46 | contentlayer build [options] 47 | ``` 48 | 49 | Transforms content into data objects that your pages and components can consume. These objects are written to files in the `.contentlayer/generated` directory. 50 | 51 | ### `dev` 52 | 53 | ```txt 54 | contentlayer dev [options] 55 | ``` 56 | 57 | Runs a build, then listens for changes to your content files, rebuilding after each change. 58 | 59 | ## Options 60 | 61 | The following options are available to run with each command. 62 | 63 | ### `config` (alias: `-c`) 64 | 65 | Use a custom config file path. Both `contentlayer.config.ts` and `contentlayer.config.js` work by default. 66 | 67 | ### `clearCache` 68 | 69 | Clears the `.contentlayer/generated` directory before running the specified command. 70 | 71 | ### `verbose` 72 | 73 | Adds more detailed output when running a command. 74 | 75 | ### `help` 76 | 77 | Generates usage instructions and options for the CLI. Does not require an associated command. 78 | -------------------------------------------------------------------------------- /content/docs/500-reference/200-next-contentlayer.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: e6e7eb3a 3 | title: next-contentlayer 4 | excerpt: Helper for improving the experience when developing with Next.js. 5 | --- 6 | 7 | The Next.js plugin provides helpers for improving the developer experience in using Contentlayer with Next.js. 8 | 9 | ## `withContentlayer` 10 | 11 | The `withContentlayer` utility automatically hooks Contentlayer into Next.js `build` and `dev` processes, which means you don't have to worry about working with the CLI when running or building your Next.js project. 12 | 13 | To enable it, add the following to your `next.config.js` file: 14 | 15 | ```js 16 | // next.config.js 17 | 18 | import { withContentlayer } from 'next-contentlayer' 19 | 20 | export default withContentlayer({}) 21 | ``` 22 | 23 | ## `useMDXComponent` 24 | 25 | If you're using MDX in your Next.js project, `useMDXComponent` makes it easy to render MDX in your layouts. 26 | 27 | Here is an example of a layout that receives a `slug `parameter, then uses it to retrieve a document and render MDX on the page. 28 | 29 | ```jsx 30 | import { useMDXComponent } from 'next-contentlayer/hooks' 31 | import { allDocs } from 'contentlayer/generated' 32 | import Button from '../components/Button' 33 | 34 | const mdxComponents = { 35 | Button, 36 | } 37 | 38 | export default function DocPage({ doc }) { 39 | const MDXContent = useMDXComponent(doc.body.code) 40 | 41 | return ( 42 |
43 |

{doc.title}

44 | 45 |
46 | ) 47 | } 48 | 49 | export const getStaticProps = ({ params: { slug } }) => { 50 | const doc = allDocs.find((doc) => doc._raw.flattenedPath === slug) 51 | return { props: { doc } } 52 | } 53 | ``` 54 | 55 | 56 | Remember to set `contentType: 'mdx'` in your config for proper [processing of MDX files](/docs/sources/files/mdx). 57 | 58 | 59 | For a more holistic view of using MDX in a project, refer to [the example Next.js project](https://github.com/contentlayerdev/next-contentlayer-example). 60 | 61 | ## `createContentlayerPlugin` 62 | 63 | The `createContentlayerPlugin` function allows you to add non-default configuration options to the next-contentlayer plugin. This is not a common use. See [`withContentlayer`](#withContentlayer) for typical use. 64 | 65 | ```js 66 | import { createContentlayerPlugin } from 'next-contentlayer' 67 | 68 | const withContentlayer = createContentlayerPlugin({ 69 | // Additional Contentlayer config options 70 | }) 71 | 72 | export default withContentlayer({ 73 | // Your Next.js config... 74 | }) 75 | ``` 76 | -------------------------------------------------------------------------------- /content/docs/500-reference/200-source-files/300-define-nested-type.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: eeeb4ab5 3 | title: defineNestedType 4 | excerpt: Technical reference for defining a nested document type schema. 5 | --- 6 | 7 | `defineNestedType` defines the schema for a reusable shape of data that can be embedded within documents. 8 | 9 | Nested types are defined within document type definitions, and therefore do not have to be included in the [`makeSource` options](/docs/reference/source-files/make-source). 10 | 11 | ## Usage 12 | 13 | ```js 14 | const SEO = defineNestedType(() => ({ 15 | name: 'SEO', 16 | fields: { 17 | title: { 18 | type: 'string', 19 | }, 20 | }, 21 | })) 22 | 23 | const Doc = defineDocumentType(() => ({ 24 | name: 'Doc', 25 | filePathPattern: '**/*.md', 26 | fields: { 27 | // ... 28 | seo: { 29 | type: 'nested', 30 | of: SEO, 31 | }, 32 | }, 33 | })) 34 | ``` 35 | 36 | ## Options 37 | 38 | ### `name` (required) 39 | 40 | Name of the document. This defines the types and functions that are generated for documents of this type. 41 | 42 | ```js 43 | const SEO = defineNestedType(() => ({ 44 | name: "SEO", 45 | // ... 46 | }) 47 | ``` 48 | 49 | Because nested types are not documents, they are not exported as individual data files. But a type definition will still be generated (as `SEO` in the usage example). 50 | 51 | ### `fields` 52 | 53 | Field definitions determine the data shape for the document type. See [the field types reference](/docs/reference/source-files/field-types) for more information. 54 | 55 | ### `description` 56 | 57 | Provides a description for the generated type definition. 58 | -------------------------------------------------------------------------------- /content/docs/500-reference/200-source-files/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: f4638f76 3 | title: '@contentlayer/source-files' 4 | excerpt: API reference for working with local files (Git CMS). 5 | collapsible: true 6 | collapsed: true 7 | --- 8 | 9 | This module provides the methods for working with local files as your content source. 10 | 11 | Typical usage involves three primary functions: 12 | 13 | - [`makeSource`](/docs/reference/source-files/make-source) controls the entire schema definition for your application. 14 | - [`defineDocumentType`](/docs/reference/source-files/define-document-type) defines the schema top-level document types (also known as _models_ or _content types_). 15 | - [`defineNestedType`](/docs/reference/source-files/define-nested-type) defines the schema for data shapes that can be reused across multiple document type definitions. 16 | 17 | --- 18 | 19 | ## Reference Guides 20 | 21 |
22 |
23 | These guides will dig into the details of each function to enable you to better understand how to work with local 24 | content. 25 |
26 |
27 | - 28 | - 29 | - 30 | - 31 |
32 |
33 | 34 | --- 35 | 36 | ## Other File Source Guides 37 | 38 |
39 |
40 | There are a few other guides to help you learn how to work with local content. 41 | 42 |
43 |
44 | - 45 | - 46 | - 47 | - 48 | - 49 |
50 |
51 | -------------------------------------------------------------------------------- /content/docs/500-reference/250-source-notion/100-make-source.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: fa487097 3 | title: makeSource 4 | excerpt: makeSource provides Contentlayer with the schema and configuration when using local files as the content source. 5 | --- 6 | 7 | `makeSource` provides Contentlayer with the schema and configuration for your application. 8 | 9 | ## Usage 10 | 11 | The code calling `makeSource` should be placed in `contentlayer.config.js`. 12 | 13 | ```ts 14 | // contentlayer.config.js 15 | 16 | import { makeSource } from 'contentlayer-source-notion' 17 | 18 | export default makeSource({ 19 | /* options */ 20 | }) 21 | ``` 22 | 23 | ## Options 24 | 25 | 26 | 27 | ### `client` 28 | 29 | 30 | 31 | The `@notionhq/client` instance used to query the Notion API. 32 | 33 | **Example:** 34 | 35 | ```js 36 | import { Client } from '@notionhq/client' 37 | import { makeSource } from 'contentlayer-source-notion' 38 | 39 | const client = new Client({ auth: process.env.NOTION_TOKEN }) 40 | 41 | export default makeSource({ 42 | client, 43 | }) 44 | ``` 45 | 46 | This would use `process.env.NOTION_TOKEN` when calling the Notion API. 47 | 48 | 49 | 50 | ### `renderer` 51 | 52 | 53 | 54 | The `@notion-render/client` instance used to transform Notion Block into HTML. 55 | 56 | **Example:** 57 | 58 | ```js 59 | import { NotionRenderer } from '@notion-render/client' 60 | import { makeSource } from 'contentlayer-source-notion' 61 | 62 | const renderer = new NotionRenderer() 63 | 64 | export default makeSource({ 65 | renderer 66 | }) 67 | ``` 68 | 69 | 70 | 71 | ### `databaseTypes` 72 | (required) 73 | 74 | 75 | 76 | Your databases definitions for your project. See [`defineDatabase`](/docs/reference/source-notion/define-database) for usage. 77 | 78 | 79 | 80 | ### `fieldOptions` 81 | 82 | 83 | 84 | Provides the ability to manipulate how fields are written when parsing the content source. 85 | 86 | **Options:** 87 | 88 | - `bodyFieldName` (default: `body`): Name of the field containing the body/content extracted when the body type is `markdown` or `mdx`. 89 | - `typeFieldName` (default: `type`): Name of the field containing the name of the document type. 90 | 91 | **Example:** 92 | 93 | ```js 94 | export default makeSource({ 95 | fieldOptions: { 96 | bodyFieldName: 'content', 97 | typeFieldName: '__typename', 98 | }, 99 | }) 100 | ``` 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /content/docs/500-reference/250-source-notion/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: c60229ba 3 | title: '@contentlayer/source-notion' 4 | excerpt: API reference for working with Notion. 5 | collapsible: true 6 | collapsed: true 7 | --- 8 | 9 | This module provides the methods for working with [Notion](https://notion.so) as your content source. 10 | 11 | Typical usage involves two primary functions: 12 | 13 | - [`makeSource`](/docs/reference/source-notion/make-source) controls the entire schema definition for your application. 14 | - [`defineDatabase`](/docs/reference/source-notion/define-database) defines how your database should be used to generate your document types. 15 | 16 | --- 17 | 18 | ## Reference Guides 19 | 20 |
21 |
22 | These guides will dig into the details of each function to enable you to better understand how to work with local 23 | content. 24 |
25 |
26 | - 27 | - 28 | - 29 |
30 |
31 | 32 | --- 33 | 34 | ## Other Notion Source Guides 35 | 36 |
37 |
38 | There are a few other guides to help you learn how to work with Notion. 39 | 40 |
41 |
42 | - 43 | - 44 | - 45 | - 46 | - 47 |
48 |
49 | -------------------------------------------------------------------------------- /content/docs/500-reference/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: abcab6a5 3 | title: API Reference 4 | show_child_cards: true 5 | excerpt: Technical API reference for Contentlayer usage. 6 | --- 7 | 8 | Technical API reference for Contentlayer, the CLI, and associated plugins. 9 | -------------------------------------------------------------------------------- /content/docs/600-integrations/100-stackbit/300-dev-server.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: f1dfe99b 3 | title: Stackbit Development Server 4 | nav_title: Development Server 5 | excerpt: Hook the Stackbit development server into Next.js and Contentlayer. 6 | --- 7 | 8 | The Stackbit development server is run using [the `stackbit dev` command](https://docs.stackbit.com/reference/stackbit-cli#dev). 9 | 10 | [`experimental-next-stackbit`](https://npmjs.com/package/experimental-next-stackbit) can be used in conjunction with [`next-contentlayer`](/docs/reference/next-contentlayer) to hook the [`contentlayer dev`](/docs/reference/cli#dev) and [`stackbit dev`](https://docs.stackbit.com/reference/stackbit-cli#dev) commands into the Next.js development server (`next dev`). See below for usage. 11 | 12 | ## `withStackbit` 13 | 14 | Next.js configuration, along with the [`withContentlayer` hook](/docs/reference/next-contentlayer#withcontentlayer) should be wrapped in the `withStackbit` method. 15 | 16 | ```js 17 | // next.config.js 18 | 19 | const { withContentlayer } = require('next-contentlayer') 20 | const { withStackbit } = require('experimental-next-stackbit') 21 | 22 | module.exports = withStackbit( 23 | withContentlayer({ 24 | reactStrictMode: true, 25 | }), 26 | ) 27 | ``` 28 | 29 | This function will automatically detect if you are in production (when `process.env.NODE_ENV` is `production`) and skip running the Stackbit development server. 30 | -------------------------------------------------------------------------------- /content/docs/600-integrations/100-stackbit/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: b1141d65 3 | title: Stackbit 4 | excerpt: Stackbit is a composable visual editor that supports multiple content sources and site frameworks. 5 | label: Experimental 6 | collapsible: true 7 | collapsed: true 8 | show_child_cards: true 9 | --- 10 | 11 | Stackbit is a composable visual editor that supports multiple content sources and site frameworks. 12 | 13 | ![Visual Editing with Stackbit](/images/stackbit-visual-editing.gif) 14 | 15 | Contentlayer can be used in sites that are Stackbit-enabled to ease the process for developers working with content in their components and pages. 16 | 17 | Contentlayer provides a method for [transforming Stackbit configuration into Contentlayer configuration](/docs/integrations/stackbit/config), so you only have to define the shape of your content in one place. 18 | 19 | ## How it Works 20 | 21 | With the appropriate packages installed, the process works like this: 22 | 23 | 1. Define the shape of your documents using [Stackbit's `models` property](https://docs.stackbit.com/reference/config/content-modeling/models). 24 | 1. Import Stackbit config object into the Contentlayer configuration file. 25 | 1. Extend any necessary properties to support your code. 26 | 1. Configure the Contentlayer source. 27 | 28 | See below for an example, and also view [the API reference](/docs/integrations/stackbit/config) for specific usage details. 29 | 30 | ## Example Project 31 | 32 | If you'd like to see this in process, check out [this example project](https://github.com/stackbit-themes/stackbit-examples/tree/main/contentlayer), which integrates Stackbit and Contentlayer in a Next.js site. 33 | 34 | The README file has setup information, but we recommend reading through the tutorial below to understand how the pieces fit together. 35 | 36 | ## Stackbit Guides 37 | 38 | The following docs will get you started integrating Stackbit and Contentlayer into your Next.js project. 39 | -------------------------------------------------------------------------------- /content/docs/600-integrations/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: ed449e02 3 | title: Integrations 4 | excerpt: Connections to other services that are not sources or frameworks 5 | show_child_cards: true 6 | --- 7 | 8 | Contentlayer has additional integrations that are not specific to content sources or site frameworks. These guides are shown below. 9 | -------------------------------------------------------------------------------- /content/docs/700-other/100-faq.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: e58c2f47 3 | title: Frequently Asked Questions 4 | nav_title: FAQ 5 | excerpt: Answers to the most common questions about Contentlayer. 6 | --- 7 | 8 | ## What problem is Contentlayer solving? 9 | 10 | Modern web frameworks don't prescribe a method for parsing content. They provide powerful page routing and rendering processes, but it's up to you to provide it with content. 11 | 12 | That's a lot of work. Without the time and energy to build a truly great processing mechanism, it's easy to quickly degrade the developer experience these tools have afforded us. 13 | 14 | Contentlayer persists the great developer experience provided by modern web frameworks by making it easy to work with content in your web project. 15 | 16 | ## Why is Contentlayer fast? 17 | 18 | Contentlayer leverages optimizations of build tools to the fullest to make processing source content a breeze. 19 | 20 | It then caches that content intelligently and builds incrementally. When you update content, Contentlayer will only build what has changed, taking advantage of work already done. 21 | 22 | ## Can I use Contentlayer with my existing tools? 23 | 24 | Contentlayer is built to be framework agnostic. Contentlayer is a content processor at its core, but provides modules for importing content from various sources, and uses plugins to provide tight integration with modern frameworks. 25 | 26 | Our docs list [current supported sources](/docs/sources) and [frameworks](/docs/environments). And if you don't see what you're looking for, [start a discussion](https://github.com/contentlayerdev/contentlayer/issues/new). We're constantly looking for new use cases to consider. 27 | 28 | ## Next.js already supports MDX. Why do I need Contentlayer? 29 | 30 | It is a misconception that Next.js inherently supports MDX. Next.js makes no inference whatsoever about where your content lives and how it should be processed. 31 | 32 | Libraries like [mdx-bundler](https://github.com/kentcdodds/mdx-bundler) and [next-mdx-remote](https://github.com/hashicorp/next-mdx-remote) provide tooling to help in processing MDX. In addition to processing MDX, Contentlayer also parses and validates content and provides auto-generated TypeScript type definitions. And it does so while prioritizing performance and developer experience. 33 | 34 | Learn more about [how Contentlayer compares to other content processors](/docs/concepts/comparison#content-processors). 35 | -------------------------------------------------------------------------------- /content/docs/700-other/200-roadmap.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: ce0cb3aa 3 | title: Roadmap 4 | excerpt: Get a picture of where we're going with upcoming releases. 5 | --- 6 | 7 | [See here](https://stackbit.notion.site/Contentlayer-Roadmap-ca6d21d6a1424b11b11692d6356c1ff7) for our roadmap. 8 | 9 | This list is not exhaustive, but meant to provide visibility into our high-level priorities. 10 | 11 | ## Adding Items to Roadmap 12 | 13 | To add something to the roadmap, first check [issues on GitHub](https://github.com/contentlayerdev/contentlayer/issues) for a relevant topic. Feel free to comment to ask for an updated status. 14 | 15 | If you don't see a relevant topic, [create one](https://github.com/contentlayerdev/contentlayer/issues/new). 16 | 17 | ## Roadmap States 18 | 19 | The roadmap shows each item in one of four states: 20 | 21 | - **Considering:** We're actively talking about the topic, but have not yet prioritized it. 22 | - **Planned:** We plan to work on these items as we release those currently in progress. These items are subject to change. 23 | - **In Progress:** Work is being done against the topic. 24 | - **Released:** The feature/change has been released in a new version. 25 | -------------------------------------------------------------------------------- /content/docs/700-other/300-changelog.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: eec1d8c0 3 | title: Changelog 4 | excerpt: Information on detailed notes for previous releases 5 | --- 6 | 7 | When we release a new version, we notify [the Discord community](https://discord.gg/rytFErsARm) and link to [detailed release notes](https://github.com/contentlayerdev/contentlayer/releases) in GitHub. 8 | -------------------------------------------------------------------------------- /content/docs/700-other/400-contributing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: d739035b 3 | title: Contributing 4 | excerpt: Multiple ways to get more involved with Contentlayer. 5 | --- 6 | 7 | There are multiple ways that you can get involved to help make Contentlayer the best way to work with content in your front end applications: 8 | 9 | - Using Contentlayer 10 | - Providing Support 11 | - Promoting Contentlayer 12 | - Building Examples 13 | - Writing Documentation 14 | - Contributing Code 15 | 16 | ## Using Contentlayer 17 | 18 | The easiest and most valuable way for you to contribute to Contentlayer is **to use it, provide feedback, and report bugs.** 19 | 20 | For feedback and general conversation, [join our Discord community](https://discord.gg/rytFErsARm). To report bugs, [open an issue on GitHub](https://github.com/contentlayerdev/contentlayer/issues/new). 21 | 22 | ## Providing Support 23 | 24 | As Contentlayer's usage grows, we'll need help keeping up with [issues on GitHub](https://github.com/contentlayerdev/contentlayer/issues). We welcome community members helping to confirm bug reports, while also adding their use cases and perspectives to feature requests. 25 | 26 | ## Promoting Contentlayer 27 | 28 | If you love Contentlayer, tell the world! Tweet about it. Write a blog post. Bring it up in a planning meeting for your next web project. Whatever it is, the more you talk about Contentlayer, the better Contentlayer becomes. 29 | 30 | ## Building Examples 31 | 32 | We have a small and focused set of examples right now. We aim to expand on these, but want to do so thoughtfully. Too many examples can be distracting for users and difficult to maintain for builders. 33 | 34 | If you'd like to build a new example or think you have a helpful one, get in touch. Send a message in #contributing on Discord, or message `seancdavis29#9241` directly, and we'll find the right example for you. 35 | 36 | ## Writing Documentation 37 | 38 | The docs are off to a good start but have a long way to go. If you are interested in writing documentation, please DM `seancdavis29#9241` on Discord. 39 | 40 | ## Contributing Code 41 | 42 | If you're interested in contributing code, please either [open an issue](https://github.com/contentlayerdev/contentlayer/issues/new) or find an issue on which you'd like to work. Add a comment stating that you'd like to work on resolving the issue, and we'll make a plan together, as the codebase is complex. 43 | -------------------------------------------------------------------------------- /content/docs/700-other/500-known-problems.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: cdffa1e4 3 | title: Known Problems 4 | excerpt: Open bugs and other known issues. 5 | --- 6 | 7 | We track all known problems with [GitHub issues](https://github.com/contentlayerdev/contentlayer/issues). 8 | 9 | Those that have confirmed as problems are identified with the `bug` label. Here is [the list of open bugs](https://github.com/contentlayerdev/contentlayer/issues?q=is%3Aissue+is%3Aopen+label%3Abug). 10 | 11 | ## Common Problems 12 | 13 | Among the issues referenced above, these are the problems most commonly encountered by Contentlayer users, along with links to the appropriate GitHub discussion(s)/issue(s): 14 | 15 | - ESLint error: `Unable to resolve path to module 'next-contentlayer'` ([GitHub Issue](https://github.com/contentlayerdev/contentlayer/issues/48#issuecomment-1097753073)) 16 | - `useLivereload` hook is needed in some cases ([GitHub Issue](https://github.com/contentlayerdev/contentlayer/issues/135)) 17 | -------------------------------------------------------------------------------- /content/docs/700-other/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: b3a6b480 3 | title: Other 4 | show_child_cards: true 5 | excerpt: A collection of other resources to help you learn more, stay up to date, and contribute. 6 | --- 7 | 8 | See below for a collection of resources to help you learn more, stay up to date, and contribute to Contentlayer. 9 | -------------------------------------------------------------------------------- /content/docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | global_id: '' # Index page doesn't use a global ID 3 | title: Contentlayer Documentation 4 | nav_title: Documentation 5 | excerpt: Contentlayer is a content preprocessor that validates and transforms your content into type-safe JSON you can easily import into your application. 6 | --- 7 | 8 | Contentlayer is a content preprocessor that validates and transforms your content into type-safe JSON you can easily import into your application. 9 | 10 |
11 | 17 | 23 |
24 | 25 | --- 26 | 27 | ## Sources 28 | 29 |
30 |
31 | Contentlayer lets you choose the source for your content. Currently only local file source is officially supported. 32 | 33 |
34 |
35 | Sources that are supported, planned, or being considered: 36 | 37 | - 38 | - 39 | - 40 | - 41 | 42 |
43 |
44 | 45 | --- 46 | 47 | ## Environments 48 | 49 |
50 |
51 | Contentlayer supports a tight integration with Next.js. Other environment support is coming soon. 52 | 53 | 54 |
55 |
56 | Frameworks that are supported, planned, or being considered: 57 | 58 | - 59 | - 60 | - 61 | - 62 | 63 |
64 |
65 | 66 | --- 67 | 68 | ## Reference 69 | 70 |
71 |
72 | Technical API reference for Contentlayer, the CLI, and associated plugins. 73 | 74 | 75 |
76 |
77 | - 78 | - 79 | - 80 |
81 |
82 | -------------------------------------------------------------------------------- /content/examples/100-nextjs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Next.js 3 | excerpt: Next.js 4 | github_repo: contentlayerdev/next-contentlayer-example 5 | open_file: posts/change-me.md 6 | --- 7 | -------------------------------------------------------------------------------- /content/examples/200-vite.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vite 3 | excerpt: Coming soon 4 | label: Coming soon 5 | --- 6 | 7 | A Vite example is coming soon 8 | -------------------------------------------------------------------------------- /content/examples/900-other.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Other Examples 3 | excerpt: Various Contentlayer Examples 4 | --- 5 | 6 | - [Contentlayer Website / Docs](https://github.com/contentlayerdev/website): The Contentlayer website & documentation is built with Contentlayer as well. It might be a good example for advanced usage patterns. 7 | -------------------------------------------------------------------------------- /content/examples/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Examples 3 | excerpt: Contentlayer Examples 4 | --- 5 | -------------------------------------------------------------------------------- /contentlayer.config.ts: -------------------------------------------------------------------------------- 1 | // TODO remove eslint-disable when fixed https://github.com/import-js/eslint-plugin-import/issues/1810 2 | // eslint-disable-next-line import/no-unresolved 3 | import { makeSource } from '@contentlayer/source-files' 4 | import highlight from 'rehype-highlight' 5 | import { contentDirPath } from './src/contentlayer/utils' 6 | import { validateDuplicateIds } from './src/utils/validate-duplicate-ids' 7 | import * as documentTypes from './src/contentlayer' 8 | 9 | export default makeSource({ 10 | contentDirPath, 11 | documentTypes, 12 | mdx: { rehypePlugins: [highlight] }, 13 | onSuccess: async (importData) => { 14 | const { allDocs } = await importData() 15 | await validateDuplicateIds(allDocs) 16 | }, 17 | }) 18 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "npm run build" 3 | publish = ".next" 4 | 5 | [[plugins]] 6 | package = "@netlify/plugin-nextjs" 7 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next-sitemap.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteUrl: 'https://contentlayer.dev', 3 | generateRobotsTxt: true, 4 | } 5 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { withContentlayer } = require('next-contentlayer') 3 | 4 | module.exports = withContentlayer({ 5 | images: { 6 | domains: ['pbs.twimg.com', 'avatars.githubusercontent.com', 'i.imgur.com'], 7 | }, 8 | headers: async () => [ 9 | { 10 | source: '/:path*', 11 | headers: [ 12 | { key: 'Cross-Origin-Opener-Policy', value: 'same-origin' }, 13 | { key: 'Cross-Origin-Embedder-Policy', value: 'same-origin' }, 14 | ], 15 | }, 16 | ], 17 | }) 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contentlayer-docs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev -p 3001 & local-ssl-proxy --source 3000 --target 3001", 7 | "build": "contentlayer build && next build && next-sitemap", 8 | "start": "next start", 9 | "check-links": "blc \"http://localhost:3001\" -roe" 10 | }, 11 | "dependencies": { 12 | "@notionhq/client": "^2.2.5", 13 | "@radix-ui/react-dropdown-menu": "^0.1.6", 14 | "@radix-ui/react-scroll-area": "^0.1.4", 15 | "@radix-ui/react-tabs": "^0.1.5", 16 | "@radix-ui/react-tooltip": "^0.1.7", 17 | "@stackblitz/sdk": "^1.7.0-alpha.3", 18 | "@tailwindcss/typography": "^0.5.2", 19 | "cheerio": "^1.0.0-rc.10", 20 | "classnames": "^2.3.1", 21 | "contentlayer-source-notion": "^0.0.1-alpha.26", 22 | "date-fns": "^2.28.0", 23 | "kbar": "^0.1.0-beta.34", 24 | "markdown-to-jsx": "^7.1.6", 25 | "next": "12.1.5", 26 | "next-sitemap": "^2.5.20", 27 | "react": "18.0.0", 28 | "react-dom": "18.0.0", 29 | "react-helmet": "^6.1.0", 30 | "shiki-twoslash": "^3.0.2", 31 | "tailwindcss-radix": "^1.6.0" 32 | }, 33 | "devDependencies": { 34 | "@netlify/plugin-nextjs": "^4.41.3", 35 | "@types/eslint": "^8.4.1", 36 | "@types/postcss-import": "^14.0.0", 37 | "@types/react": "^18.0.3", 38 | "@types/react-helmet": "^6.1.5", 39 | "autoprefixer": "^10.4.4", 40 | "broken-link-checker": "^0.7.8", 41 | "contentlayer": "^0.3.3", 42 | "eslint": "8.13.0", 43 | "eslint-config-next": "12.1.5", 44 | "eslint-plugin-import": "^2.26.0", 45 | "gray-matter": "^4.0.3", 46 | "local-ssl-proxy": "^1.3.0", 47 | "mdast-util-mdx": "^2.0.0", 48 | "mdast-util-to-markdown": "^1.3.0", 49 | "next-contentlayer": "^0.3.3", 50 | "postcss": "^8.4.12", 51 | "postcss-import": "^14.1.0", 52 | "prettier": "^2.6.2", 53 | "prettier-plugin-tailwindcss": "^0.1.8", 54 | "rehype-highlight": "^5.0.2", 55 | "tailwindcss": "^3.0.24", 56 | "typescript": "4.9.4" 57 | }, 58 | "packageManager": "yarn@3.2.0", 59 | "resolutions": { 60 | "@stackblitz/sdk@1.7.0": "patch:@stackblitz/sdk@npm:1.7.0-alpha.3#.yarn/patches/@stackblitz-sdk-npm-1.7.0-alpha.3-1f8676ad43.patch" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'tailwindcss/nesting': {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-167x167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-167x167.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/favicon/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/favicon-128x128.png -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/favicon-196x196.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /public/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /public/fonts/virgil.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/fonts/virgil.woff2 -------------------------------------------------------------------------------- /public/images/beta-launch-post-meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/beta-launch-post-meta.png -------------------------------------------------------------------------------- /public/images/content-as-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/content-as-data.png -------------------------------------------------------------------------------- /public/images/content-is-hard-meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/content-is-hard-meta.png -------------------------------------------------------------------------------- /public/images/content-timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/content-timeline.png -------------------------------------------------------------------------------- /public/images/contentlayer-in-five-minutes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/contentlayer-in-five-minutes.png -------------------------------------------------------------------------------- /public/images/intro-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/intro-thumbnail.jpg -------------------------------------------------------------------------------- /public/images/local-data-transformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/local-data-transformation.png -------------------------------------------------------------------------------- /public/images/logos/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/images/logos/contentful.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /public/images/logos/mdx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/logos/notion.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/logos/remix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/logos/sanity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/images/logos/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/images/notion-contentlayer-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/notion-contentlayer-source.png -------------------------------------------------------------------------------- /public/images/notion/database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/notion/database.png -------------------------------------------------------------------------------- /public/images/notion/integration_granular_permissions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/notion/integration_granular_permissions.gif -------------------------------------------------------------------------------- /public/images/performance-comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/performance-comparison.png -------------------------------------------------------------------------------- /public/images/playground-hint-dev-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/playground-hint-dev-server.png -------------------------------------------------------------------------------- /public/images/playground-hint-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/playground-hint-edit.png -------------------------------------------------------------------------------- /public/images/playground-hint-updates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/playground-hint-updates.png -------------------------------------------------------------------------------- /public/images/post-feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/post-feed.png -------------------------------------------------------------------------------- /public/images/post-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/post-layout.png -------------------------------------------------------------------------------- /public/images/stackbit-visual-editing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentlayerdev/website/861e79d100ebcc41de89d8bcb5d522b37dcf46b0/public/images/stackbit-visual-editing.gif -------------------------------------------------------------------------------- /scripts/generate-page-ids.mjs: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | import glob from 'glob' 3 | import fs from 'fs' 4 | import matter from 'gray-matter' 5 | 6 | const pages = glob.sync('./content/docs/**/*.md*', { ignore: './content/docs/index.md*' }) 7 | 8 | function generateId() { 9 | const id = crypto.randomBytes(4).toString('hex') 10 | if (id.match(/^\d/)) return generateId() 11 | return id 12 | } 13 | 14 | const skipped = [] 15 | 16 | pages.forEach((pagePath) => { 17 | const id = generateId() 18 | const rawContent = fs.readFileSync(pagePath, 'utf8') 19 | const { data } = matter(rawContent) 20 | if (data.global_id) { 21 | skipped.push(pagePath) 22 | return 23 | } 24 | if (!rawContent.startsWith('---\n')) { 25 | throw new Error(`[Error] ${pagePath} does not have frontmatter`) 26 | } 27 | 28 | const newContent = rawContent.replace(/^---\n/, `---\nglobal_id: ${id}\n`) 29 | fs.writeFileSync(pagePath, newContent) 30 | console.log(`[New ID] ${pagePath} -> ${id}`) 31 | }) 32 | 33 | if (skipped.length) { 34 | console.log(`[Skipped] ${skipped.length} with existing ids`) 35 | } 36 | -------------------------------------------------------------------------------- /src/components/ColorSchemeContext.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FC, 3 | useEffect, 4 | Dispatch, 5 | SetStateAction, 6 | createContext, 7 | useState, 8 | useContext, 9 | useMemo, 10 | useCallback, 11 | } from 'react' 12 | import { ColorScheme } from '../utils/syntax-highlighting' 13 | 14 | const ColorSchemeContext = createContext<'light' | 'dark' | 'system'>('light') 15 | const UpdateColorSchemeContext = createContext<(colorScheme: 'light' | 'dark' | 'system') => void>(() => {}) 16 | 17 | export const useColorScheme = () => useContext(ColorSchemeContext) 18 | export const useUpdateColorScheme = () => useContext(UpdateColorSchemeContext) 19 | 20 | export const ColorSchemeProvider: FC> = ({ children }) => { 21 | const initalColorScheme = useMemo( 22 | () => 23 | typeof window !== 'undefined' ? (localStorage.getItem('theme') as ColorScheme | null) ?? 'system' : 'system', 24 | [], 25 | ) 26 | const [colorScheme, setColorScheme] = useState<'light' | 'dark' | 'system'>(initalColorScheme) 27 | 28 | const updateColorScheme = useCallback( 29 | (newColorScheme: 'light' | 'dark' | 'system') => { 30 | if (newColorScheme === colorScheme) return 31 | 32 | setColorScheme(newColorScheme) 33 | 34 | if (newColorScheme === 'system') { 35 | if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { 36 | document.documentElement.classList.add('dark') 37 | } else { 38 | document.documentElement.classList.remove('dark') 39 | } 40 | localStorage.removeItem('theme') 41 | } else { 42 | if (newColorScheme === 'dark') { 43 | document.documentElement.classList.add('dark') 44 | } else { 45 | document.documentElement.classList.remove('dark') 46 | } 47 | localStorage.theme = newColorScheme 48 | } 49 | }, 50 | [colorScheme], 51 | ) 52 | 53 | useEffect(() => { 54 | if (typeof window === 'undefined') return 55 | if (colorScheme === 'system') { 56 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => updateColorScheme('system')) 57 | } 58 | }, [colorScheme, updateColorScheme]) 59 | 60 | return ( 61 | 62 | {children} 63 | 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /src/components/blog/BenchmarkResults.tsx: -------------------------------------------------------------------------------- 1 | const TableHeadCell: React.FC< 2 | React.PropsWithChildren<{ 3 | style?: React.CSSProperties 4 | className?: string 5 | }> 6 | > = ({ children, className, style }) => { 7 | return ( 8 | 9 | {children} 10 | 11 | ) 12 | } 13 | 14 | const TableCell: React.FC< 15 | React.PropsWithChildren<{ 16 | style?: React.CSSProperties 17 | className?: string 18 | }> 19 | > = ({ children, className, style }) => { 20 | return ( 21 | 22 | {children} 23 | 24 | ) 25 | } 26 | 27 | const ResultCell: React.FC> = ({ children, success }) => { 28 | let classes = `font-mono` 29 | if (success) classes += ` font-bold text-green-500` 30 | 31 | return {children} 32 | } 33 | 34 | export const BenchmarkResults: React.FC = () => { 35 | return ( 36 |
37 |
38 | 39 | 40 | 41 | 42 | Cold Build 43 | Cached Build 44 | 45 | 46 | 47 | 48 | Next.js + Contentlayer 49 | 25.73 s 50 | 16.29 s 51 | 52 | 53 | Next.js + DIY Content 54 | 44.48 s 55 | 39.27 s 56 | 57 | 58 | Gatsby 59 | 46.59 s 60 | 25.73 s 61 | 62 | 63 |
64 |
65 |
66 | Smaller is better/faster. Used machine:{' '} 67 | 72 | GitHub Actions 73 | 74 | . 75 |
76 |
77 | ) 78 | } 79 | -------------------------------------------------------------------------------- /src/components/blog/BlogDetails.tsx: -------------------------------------------------------------------------------- 1 | import { Post } from 'contentlayer/generated' 2 | import { FC } from 'react' 3 | import Link from 'next/link' 4 | import { Icon } from 'src/components/common/Icon' 5 | import { format } from 'date-fns' 6 | 7 | export const BlogDetails: FC<{ post: Post; className?: string }> = ({ post, className }) => { 8 | return ( 9 |
10 |

11 | 12 | 13 | 14 | {format(new Date(post.date), 'MMMM dd, yyyy')} 15 |

16 |

17 | 18 | 19 | 20 |

21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/blog/BlogHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Post } from 'contentlayer/generated' 2 | import { FC, useEffect, useState } from 'react' 3 | import { BlogDetails } from 'src/components/blog/BlogDetails' 4 | import Link from 'next/link' 5 | import { Icon } from 'src/components/common/Icon' 6 | import Image from 'next/image' 7 | import { format } from 'date-fns' 8 | 9 | export const BlogHeader: FC<{ post: Post }> = ({ post }) => { 10 | const [top, setTop] = useState(true) 11 | 12 | useEffect(() => { 13 | const handleScroll = () => setTop(window.scrollY <= 50) 14 | handleScroll() 15 | window.addEventListener('scroll', handleScroll) 16 | return () => { 17 | window.removeEventListener('scroll', handleScroll) 18 | } 19 | }, []) 20 | 21 | return ( 22 | <> 23 |
24 |
25 |

26 | {post.title} 27 |

28 |

29 | 30 | 31 | 32 | {format(new Date(post.date), 'MMMM dd, yyyy')} 33 |

34 |
35 |
36 | 53 | 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /src/components/blog/BlogPreview.tsx: -------------------------------------------------------------------------------- 1 | import { Post } from 'contentlayer/generated' 2 | import { BlogDetails } from 'src/components/blog/BlogDetails' 3 | import { Heading } from '../../components/landing-page/Heading' 4 | import { Paragraph } from '../../components/landing-page/Paragraph' 5 | import { ChevronLink } from '../../components/common/ChevronLink' 6 | import { FC } from 'react' 7 | import Image from 'next/image' 8 | import { Card } from '../common/Card' 9 | import Link from 'next/link' 10 | import { Icon } from 'src/components/common/Icon' 11 | import { format } from 'date-fns' 12 | 13 | export const BlogPreview: FC<{ post: Post }> = ({ post }) => { 14 | return ( 15 | 16 |
17 | 18 | 19 | {post.cover_image.alt} 27 | 28 | 29 |
30 |
31 | 32 | {post.title} 33 | 34 |
35 |

36 | 37 | 38 | 39 | {format(new Date(post.date), 'MMMM dd, yyyy')} 40 |

41 |

42 | 43 | 44 | 45 | 46 | {post.authors.map(({ name }, index) => ( 47 | 48 | {index > 0 && ', '} 49 | {name} 50 | 51 | ))} 52 | 53 |

54 |
55 | {post.excerpt} 56 | 57 |
58 |
59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /src/components/blog/BulletList.tsx: -------------------------------------------------------------------------------- 1 | import { Icon } from '../common/Icon' 2 | 3 | export const BulletList: React.FC> = ({ columns, children }) => { 4 | return
    {children}
5 | } 6 | 7 | export const BulletListItem: React.FC> = ({ type, children }) => { 8 | return ( 9 |
  • 10 | {type === 'check' && ( 11 | 12 | 13 | 14 | )} 15 | {type === 'cross' && ( 16 | 17 | 18 | 19 | )} 20 | {children} 21 |
  • 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/blog/Playground.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useState, useEffect, useRef } from 'react' 2 | import stackblitz, { type VM } from '@stackblitz/sdk' 3 | 4 | export const Playground: FC<{ githubRepo: string; openFile?: string | '' }> = ({ githubRepo, openFile }) => { 5 | const ref = useRef(null) 6 | const [vm, setVm] = useState(undefined) 7 | 8 | useEffect(() => { 9 | if (ref.current && vm === undefined) { 10 | stackblitz 11 | .embedGithubProject(ref.current, 'contentlayerdev/next-contentlayer-example', { 12 | height: 700, 13 | showSidebar: true, 14 | openFile: openFile, 15 | }) 16 | .then((_) => setVm(_)) 17 | } 18 | }, [ref, openFile, vm]) 19 | 20 | return ( 21 |
    22 |
    23 |
    24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/components/blog/RelatedPosts.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { allPosts, Post, RelatedPost } from 'contentlayer/generated' 3 | import { Card } from '../common/Card' 4 | import { BlogPreview } from './BlogPreview' 5 | 6 | export const RelatedPosts: FC<{ posts: RelatedPost[] }> = ({ posts }) => { 7 | return ( 8 |
    9 |

    Related Posts

    10 |
    11 | {posts.map(({ slug }, index) => { 12 | const post = allPosts.find((_) => _.slug === slug)! 13 | return 14 | })} 15 |
    16 |
    17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/components/blog/TLDR.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from '../common/Card' 2 | import { Icon } from '../common/Icon' 3 | 4 | export const TLDR: React.FC> = ({ children }) => { 5 | return ( 6 | 7 |
    {children}
    8 |
    9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/components/common/Arrow/CurvedLong.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | export const ArrowCurvedLong: FC = () => { 4 | return ( 5 | 6 | 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/common/Arrow/CurvedShort.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | export const ArrowCurvedShort: FC = () => { 4 | return ( 5 | 6 | 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/common/Arrow/StraightDashed.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | export const ArrowStraightDashed: FC = () => { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/components/common/Arrow/StraightLong.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | export const ArrowStraightLong: FC = () => { 4 | return ( 5 | 6 | 7 | 8 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/components/common/Arrow/StraightShort.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | export const ArrowStraightShort: FC = () => { 4 | return ( 5 | 6 | 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/common/Arrow/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | 3 | import { ArrowStraightLong } from './StraightLong' 4 | import { ArrowStraightShort } from './StraightShort' 5 | import { ArrowStraightDashed } from './StraightDashed' 6 | import { ArrowCurvedLong } from './CurvedLong' 7 | import { ArrowCurvedShort } from './CurvedShort' 8 | import { ArrowLoopedLong } from './LoopedLong' 9 | import { ArrowLoopedShort } from './LoopedShort' 10 | 11 | export type ArrowType = 12 | | 'straight-long' 13 | | 'straight-short' 14 | | 'straight-dashed' 15 | | 'curved-long' 16 | | 'curved-short' 17 | | 'looped-long' 18 | | 'looped-short' 19 | 20 | const arrows = { 21 | 'straight-long': ArrowStraightLong, 22 | 'straight-short': ArrowStraightShort, 23 | 'straight-dashed': ArrowStraightDashed, 24 | 'curved-long': ArrowCurvedLong, 25 | 'curved-short': ArrowCurvedShort, 26 | 'looped-long': ArrowLoopedLong, 27 | 'looped-short': ArrowLoopedShort, 28 | } 29 | 30 | export const Arrow: FC<{ type: ArrowType; className: string }> = ({ type, className }) => { 31 | const Arrow = arrows[type] 32 | return ( 33 |
    34 | 35 |
    36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /src/components/common/Author.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import Image from 'next/image' 3 | import Link from 'next/link' 4 | 5 | export const Author: FC<{ name: string; handle: string; avatar: string }> = ({ name, handle, avatar }) => { 6 | return ( 7 |
    8 |
    9 |
    10 | {name} 11 |
    12 |
    13 |
    14 |

    {name}

    15 | 16 | 17 | {'@' + handle} 18 | 19 | 20 |
    21 |
    22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/common/Button.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import classnames from 'classnames' 3 | import { Icon, IconName } from './Icon' 4 | import Link from 'next/link' 5 | import { isExternalUrl } from '../../utils/helpers' 6 | 7 | const themeClasses = { 8 | primary: 9 | 'bg-violet-600 text-violet-50 border-violet-800 hover:bg-violet-500 dark:bg-violet-600 dark:border-violet-700 dark:hover:bg-violet-500 dark:hover:border-violet-600', 10 | secondary: 11 | 'bg-violet-100 text-violet-800 border-violet-200 hover:bg-violet-50 dark:text-violet-300 dark:border-violet-500/30 dark:hover:bg-violet-500/30 dark:bg-violet-500/20', 12 | } 13 | 14 | export const Button: FC<{ 15 | label: string 16 | action?: () => void 17 | theme?: 'primary' | 'secondary' 18 | href?: string 19 | icon?: IconName 20 | }> = ({ label, action, href, theme = 'primary', icon }) => { 21 | const sharedClasses = 22 | 'px-6 py-2 flex justify-center items-center space-x-3 rounded-md border font-medium focus:outline-none focus:ring-2 focus:ring-violet-300 dark:focus:ring-violet-900' 23 | 24 | if (href) { 25 | return ( 26 | 27 | 34 | {label} 35 | {icon && ( 36 | 37 | 38 | 39 | )} 40 | 41 | 42 | ) 43 | } else { 44 | return ( 45 | 53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/common/Callout.tsx: -------------------------------------------------------------------------------- 1 | import { FC, ReactNode } from 'react' 2 | import { Icon } from './Icon' 3 | 4 | export const Callout: React.FC<{ children: ReactNode; className?: string | '' }> = ({ children, className }) => { 5 | return ( 6 |
    9 |
    10 |
    11 | 12 |
    13 |
    {children}
    14 |
    15 |
    16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/components/common/Card.tsx: -------------------------------------------------------------------------------- 1 | import { FC, ReactNode } from 'react' 2 | import classNames from 'classnames' 3 | 4 | export const Card: FC<{ children: ReactNode; className?: string; shadow?: boolean; dark?: boolean }> = ({ 5 | children, 6 | className, 7 | shadow = false, 8 | dark = false, 9 | }) => { 10 | return ( 11 |
    19 | {children} 20 |
    21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/common/ChevronLink.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import Link from 'next/link' 3 | import { Icon } from './Icon' 4 | 5 | const isExternalUrl = (link: string): boolean => !link.startsWith('/') 6 | 7 | export const ChevronLink: FC<{ label: string; url: string }> = ({ label, url }) => { 8 | if (isExternalUrl(url)) { 9 | return ( 10 | 16 | {label} 17 | 18 | 19 | 20 | 21 | ) 22 | } else { 23 | return ( 24 | 25 | 26 | {label} 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/components/common/Container.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import Head from 'next/head' 3 | import { useRouter } from 'next/router' 4 | import { SearchProvider } from '../SearchContext' 5 | import { MainNavigation } from './MainNavigation' 6 | import { Footer } from './Footer' 7 | 8 | export const Container: FC = ({ children, ...customMeta }) => { 9 | const router = useRouter() 10 | 11 | const baseUrl = `https://www.contentlayer.dev` 12 | 13 | const meta = { 14 | title: 'Contentlayer makes content easy for developers', 15 | description: 16 | 'Contentlayer is a content SDK that validates and transforms your content into type-safe JSON data you can easily import into your application.', 17 | url: customMeta.urlPath ? `${baseUrl}${customMeta.urlPath}` : baseUrl, 18 | name: 'Contentlayer', 19 | image: customMeta.imagePath ? `${baseUrl}${customMeta.imagePath}` : `${baseUrl}/images/beta-launch-post-meta.png`, 20 | type: 'website', 21 | ...customMeta, 22 | } 23 | const jsonLd = { 24 | '@context': 'http://www.schema.org', 25 | '@type': 'WebSite', 26 | name: meta.name, 27 | url: meta.url, 28 | } 29 | 30 | return ( 31 | <> 32 | 33 | {meta.title} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |