├── .gitignore
├── LICENSE
├── README.md
├── astro
├── .env.template
├── .gitignore
├── .nvmrc
├── .stackblitzrc
├── README.md
├── astro.config.mjs
├── netlify.toml
├── package-lock.json
├── package.json
├── public
│ ├── assets
│ │ ├── blog
│ │ │ └── example-blog
│ │ │ │ └── alex-bertha-7VAXeUIn3tI-unsplash.jpg
│ │ └── logo.svg
│ ├── favicon.svg
│ └── robots.txt
├── sandbox.config.json
├── src
│ ├── components
│ │ ├── ArticleSchema.astro
│ │ ├── BaseHead.astro
│ │ ├── BlogPost.astro
│ │ ├── BlogPostPreview.astro
│ │ ├── CodeBlock
│ │ │ ├── code-block-styles.ts
│ │ │ └── code-block.ts
│ │ ├── Container.astro
│ │ └── SanityPortableText.astro
│ ├── data
│ │ └── settings.js
│ ├── layouts
│ │ ├── Base.astro
│ │ ├── Page.astro
│ │ └── Post.astro
│ ├── lib
│ │ ├── api.js
│ │ └── sanityPortableText.js
│ ├── pages
│ │ ├── 404.astro
│ │ ├── blog
│ │ │ ├── [slug].astro
│ │ │ ├── category
│ │ │ │ └── [slug].astro
│ │ │ └── index.astro
│ │ └── index.astro
│ ├── styles
│ │ ├── global.css
│ │ └── reset.css
│ └── utils
│ │ └── helpers.js
└── tsconfig.json
├── package-lock.json
├── package.json
└── sanity
├── .gitignore
├── .npmignore
├── README.md
├── config
├── .checksums
└── @sanity
│ ├── data-aspects.json
│ ├── default-layout.json
│ ├── default-login.json
│ ├── form-builder.json
│ └── vision.json
├── package.json
├── plugins
└── .gitkeep
├── sanity.json
├── schemas
├── author.js
├── blockContent.js
├── category.js
├── post.js
└── schema.js
├── static
├── .gitkeep
└── favicon.ico
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Mac files
2 | .DS_Store
3 |
4 | # Dependency directories
5 | /node_modules
6 |
7 | # lerna files
8 | /lerna-debug.log
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 White Pine Studio, LLC
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Astro Sanity Minimal Starter
2 |
3 | ## [Demo Site](https://astro-sanity-minimal-starter.netlify.app/)
4 |
5 | If you haven't heard about [Astro](https://astro.build) yet you're missing out. This starter repo gives you a blog website with a headless CMS setup using [Sanity.io](https://sanity.io).
6 |
7 | Don't be fooled by the simplicity of this starter either. With excellent SEO built in, Astro's excellent developer environment, and performance best practices you're getting one of the best foundations for a personal blog with posts written in markdown. The reason for making such a bare bones starter in terms of styles is that it has what you need out of the box for SEO and site structure but it makes no assumptions about how you want it look and behave.
8 |
9 | Sometimes starters can feel like they are giving us way too much and you'll spend a day picking apart half of the code to get to the setup you actually wanted. Want to use vanilla CSS instead of Tailwind or Scss? No problem!
10 |
11 | ## Features
12 |
13 | - A customizable headless CMS with [Sanity.io](https://sanity.io)
14 | - Excellent SEO Built In
15 | - Open Graph Tags (Twitter, Facebook, etc.)
16 | - User Declared Canonical URLs
17 | - RSS Feed
18 | - XML Sitemap
19 | - Robots.txt
20 | - JSON LD Schema
21 | - Extremely minimal styles
22 | - Performant Google Fonts Setup
23 |
24 | ## Get Started
25 |
26 | Watch my [step-by-step tutorial on YouTube](https://youtu.be/-jAWLTfsSQw)
27 |
28 | **Or**
29 |
30 |
31 | Follow the manual steps below:
32 |
33 | ### Initial Sanity Setup
34 |
35 | 1. [Create a repo from this template](https://github.com/jaydanurwin/astro-sanity-minimal-starter/generate)
36 | 2. If you don't have the Sanity CLI already run `npm install -g @sanity/cli`
37 | 3. Open a terminal from root folder of the project
38 | 4. `cd sanity`
39 | 5. `sanity install`
40 | 6. `sanity init` and follow the prompts (you will need to create a Sanity account if you haven't already)
41 | 8. Navigate to the astro folder
42 | 7. Rename `.env.template` to `.env` and
43 | 8. Replace the PUBLIC_SANITY_PROJECT_ID value with your token ID
44 |
45 | **Note:** You will want to add http://localhost:3000 to your allowed CORS orgins in your Sanity project settings at [https://manage.sanity.io](https://manage.sanity.io)
46 |
47 | ### To run your Sanity Studio
48 |
49 | 1. Open a terminal located at the sanity folder
50 | 2. `sanity install`
51 | 3. `sanity start`
52 | 4. Your studio should be running on [http://localhost:3333](http://localhost:3333)
53 | 5. Open [http://localhost:3333](http://localhost:3333) and copy the project ID from the Project info section
54 |
55 | ### To run your Astro site
56 |
57 | 1. Open a terminal located at the astro folder
58 | 2. `npm install`
59 | 3. `npm start`
60 | 4. Your Astro site should be running on [http://localhost:3000](http://localhost:3000)
61 | 5. `npm run build` to build to production locally
62 |
--------------------------------------------------------------------------------
/astro/.env.template:
--------------------------------------------------------------------------------
1 | PUBLIC_SANITY_PROJECT_ID="8hj1t7km"
2 | PUBLIC_SANITY_DATASET="production"
3 | PUBLIC_SANITY_API_VERSION="v2021-03-25"
4 | PUBLIC_SANITY_READ_TOKEN=""
5 | PUBLIC_SANITY_GRAPHQL_URL="https://8hj1t7km.api.sanity.io/v1/graphql/production/default"
--------------------------------------------------------------------------------
/astro/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist
3 |
4 | # dependencies
5 | node_modules/
6 | .snowpack/
7 |
8 | # logs
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # environment variables
14 | .env
15 | .env.production
16 |
17 | # macOS-specific files
18 | .DS_Store
19 |
--------------------------------------------------------------------------------
/astro/.nvmrc:
--------------------------------------------------------------------------------
1 | v14.15.1
--------------------------------------------------------------------------------
/astro/.stackblitzrc:
--------------------------------------------------------------------------------
1 | {
2 | "startCommand": "npm start",
3 | "env": {
4 | "ENABLE_CJS_IMPORTS": true
5 | }
6 | }
--------------------------------------------------------------------------------
/astro/README.md:
--------------------------------------------------------------------------------
1 | [](https://app.netlify.com/sites/astro-minimal-starter/deploys)
2 |
3 | # astro-minimal-starter
4 |
5 | ## [Demo Site](https://astro-minimal-starter.netlify.app/)
6 |
7 | If you haven't heard about [Astro](https://astro.build) yet you're missing out. This starter repo gives you a blog website with posts written in Markdown, all powered by Astro.
8 |
9 | Don't be fooled by the simplicity of this starter either. With excellent SEO built in, Astro's excellent developer environment, and performance best practices you're getting one of the best foundations for a personal blog with posts written in markdown. The reason for making such a bare bones starter in terms of styles is that it has what you need out of the box for SEO and site structure but it makes no assumptions about how you want it look and behave.
10 |
11 | Sometimes starters can feel like they are giving us way too much and you'll spend a day picking apart half of the code to get to the setup you actually wanted. Want to vanilla CSS instead of Tailwind or Scss? No problem!
12 |
13 | ## Features
14 |
15 | - Excellent SEO Built In
16 | - Open Graph Tags (Twitter, Facebook, etc.)
17 | - User Declared Canonical URLs
18 | - RSS Feed
19 | - XML Sitemap
20 | - Robots.txt
21 | - JSON LD Schema
22 | - Markdown based blog posts with featured images
23 | - Extremely minimal styles
24 | - Performant Google Fonts Setup
25 |
26 | ## Get Started
27 |
28 | ```
29 | # Install Dependencies
30 |
31 | npm i
32 |
33 | # Run Site locally
34 |
35 | npm start
36 |
37 | # Build Site
38 |
39 | npm run build
40 | ```
41 |
42 | Navigate to `src/data/site.js` and edit to match your site's information.
43 |
44 | Start writing new blog posts in Markdown at `src/pages/blog/`
45 |
--------------------------------------------------------------------------------
/astro/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'astro/config';
2 | import sitemap from '@astrojs/sitemap';
3 |
4 | import sanity from 'astro-sanity';
5 |
6 | // https://astro.build/config
7 | export default defineConfig({
8 | site: 'https://astro-sanity-minimal-starter.netlify.app/',
9 | // Your public domain, e.g.: https://my-site.dev/
10 | integrations: [
11 | sitemap(),
12 | sanity({
13 | projectId: '8hj1t7km',
14 | dataset: 'production',
15 | apiVersion: 'v2021-03-25',
16 | useCdn: true,
17 | }),
18 | ],
19 | });
20 |
--------------------------------------------------------------------------------
/astro/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "npm run build"
3 | publish = "dist"
--------------------------------------------------------------------------------
/astro/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "astro-sanity-minimal-starter",
3 | "version": "v3.2.0",
4 | "private": false,
5 | "scripts": {
6 | "dev": "astro dev",
7 | "start": "astro dev",
8 | "build": "astro build"
9 | },
10 | "devDependencies": {
11 | "@astrojs/sitemap": "^0.1.0",
12 | "astro": "^1.2.4",
13 | "astro-sanity": "^1.0.0",
14 | "date-fns": "^2.23.0",
15 | "dotenv": "^10.0.0",
16 | "lit": "^2.3.1",
17 | "prismjs": "^1.25.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/astro/public/assets/blog/example-blog/alex-bertha-7VAXeUIn3tI-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treefarmstudio/astro-sanity-minimal-starter/1e89323443682f6a6d571f37f8fc50a5c901f92c/astro/public/assets/blog/example-blog/alex-bertha-7VAXeUIn3tI-unsplash.jpg
--------------------------------------------------------------------------------
/astro/public/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/astro/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/astro/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
--------------------------------------------------------------------------------
/astro/sandbox.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "infiniteLoopProtection": true,
3 | "hardReloadOnChange": false,
4 | "view": "browser",
5 | "template": "node",
6 | "container": {
7 | "port": 3000,
8 | "startScript": "start",
9 | "node": "14"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/astro/src/components/ArticleSchema.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import settings from '../data/settings.js';
3 |
4 | const { title, permalink, publishDate, ogImageUrl, description } = Astro.props;
5 |
6 | const ldData = {
7 | "@context": "https://schema.org",
8 | "@type": "Article",
9 | "publisher": {
10 | "@type": "Organization",
11 | "name": `${settings.name} Blog`,
12 | "url": `${settings.url}/blog/`,
13 | },
14 | "author": {
15 | "@type": "Person",
16 | "name": `${settings.name}`,
17 | "image": {
18 | "@type": "ImageObject",
19 | "url": `${settings.url}/assets/img/favicon.png`,
20 | "width": 512,
21 | "height": 512
22 | },
23 | "url": "https://example.com/about/",
24 | "sameAs": [
25 | `${settings.url}`,
26 | `${settings.facebookURL}`,
27 | `${settings.twitterURL}`,
28 | `${settings.instagramURL}`
29 | ]
30 | },
31 | "headline": `${title}`,
32 | "url": `${permalink}`,
33 | "datePublished": `${publishDate}`,
34 | "image": {
35 | "@type": "ImageObject",
36 | "url": `${ogImageUrl}`,
37 | "width": 1200,
38 | },
39 | "description": `${description}`,
40 | "mainEntityOfPage": {
41 | "@type": "WebPage",
42 | "@id": `${settings.url}/blog/`
43 | }
44 | }
45 | ---
46 |
47 |
--------------------------------------------------------------------------------
/astro/src/components/BaseHead.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import settings from "../data/settings.js";
3 |
4 | import '../styles/reset.css';
5 | import '../styles/global.css';
6 |
7 | export interface Props {
8 | title?: string;
9 | description?: string;
10 | canonicalURL?: URL | string;
11 | image?: string;
12 | }
13 |
14 | const title = Astro.props.title || settings.title;
15 | const description = Astro.props.description || settings.description;
16 | const canonicalURL = Astro.props.canonicalURL || new URL(Astro.url.pathname, Astro.site);
17 | const image = new URL(Astro.props.image || './social.png', Astro.site);
18 | ---
19 |
20 |
21 |
22 |
23 |
24 |
25 |
{title || settings.title}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
57 |
58 |
62 |
63 |
68 |
69 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/astro/src/components/BlogPost.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import SanityPortableText from './SanityPortableText.astro'
3 | import { getSanityImageURL, formatBlogPostDate } from '../utils/helpers.js'
4 | import Container from './Container.astro';
5 |
6 | export interface Props {
7 | post: any;
8 | }
9 |
10 | const { post } = Astro.props;
11 | // const blogPostBody = await convertBlockContentToMarkdown(post.bodyRaw);
12 | ---
13 |
14 |
15 |
16 | {post.mainImage && }
17 | {post.title}
18 | {formatBlogPostDate(post.publishedAt)}
19 | {post.author && By {post.author.name}
}
20 |
21 |
22 |
23 |
24 |
34 |
35 |
--------------------------------------------------------------------------------
/astro/src/components/BlogPostPreview.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { getSanityImageURL, formatBlogPostDate } from '../utils/helpers.js'
3 |
4 | export interface Props {
5 | post: any;
6 | }
7 |
8 | const { post } = Astro.props;
9 | ---
10 |
11 |
12 |
13 | {post.mainImage && }
14 | {post.title}
15 | {formatBlogPostDate(post.publishedAt)}
16 | {post.excerpt}
17 |
18 |
19 |
20 |
56 |
--------------------------------------------------------------------------------
/astro/src/components/CodeBlock/code-block-styles.ts:
--------------------------------------------------------------------------------
1 | import { css } from "lit";
2 |
3 | export const codeBlockStyles = css`
4 | #code-block__container {
5 | position: relative;
6 | }
7 | button {
8 | z-index: 2;
9 | margin: 0;
10 | padding: 0.5rem;
11 | position: absolute;
12 | top: 0.5rem;
13 | right: 0.5rem;
14 | background: #e2e8f022;
15 | border: none;
16 | border-radius: 0.25rem;
17 | color: var(--color-white, #ffffff);
18 | font-weight: 600;
19 | line-height: 1;
20 | cursor: pointer;
21 | }
22 | pre {
23 | display: flex;
24 | position: relative;
25 | padding: 1rem;
26 | border-radius: 0.25rem;
27 | box-shadow: 0 0 1rem rgb(0 0 0 / 28%);
28 | padding-top: 2.5rem;
29 | }
30 |
31 | #language-tag {
32 | position: absolute;
33 | top: 0;
34 | left: 1rem;
35 | background-color: #f8f8f2;
36 | color: #212121;
37 | text-shadow: none;
38 | padding: 0.25rem 0.5rem;
39 | border-radius: 0 0 0.25rem 0.25rem;
40 | font-size: 0.75rem;
41 | }
42 |
43 | /* PrismJS 1.23.0
44 | https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript+bash+dart+json+markdown */
45 | /**
46 | * okaidia theme for JavaScript, CSS and HTML
47 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/
48 | * @author ocodia
49 | */
50 |
51 | code[class*="language-"],
52 | pre[class*="language-"] {
53 | color: #f8f8f2;
54 | background: none;
55 | text-shadow: 0 1px rgba(0, 0, 0, 0.3);
56 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
57 | font-size: 1rem;
58 | text-align: left;
59 | white-space: pre;
60 | word-spacing: normal;
61 | word-break: normal;
62 | word-wrap: normal;
63 | line-height: 1.5;
64 |
65 | -moz-tab-size: 4;
66 | -o-tab-size: 4;
67 | tab-size: 4;
68 |
69 | -webkit-hyphens: none;
70 | -moz-hyphens: none;
71 | -ms-hyphens: none;
72 | hyphens: none;
73 | }
74 |
75 | /* Code blocks */
76 | pre[class*="language-"] {
77 | /* padding: 1em; */
78 | margin: 0.5em 0;
79 | overflow: auto;
80 | border-radius: 0.3em;
81 | }
82 |
83 | :not(pre) > code[class*="language-"],
84 | pre[class*="language-"] {
85 | background: #212121;
86 | }
87 |
88 | /* Inline code */
89 | :not(pre) > code[class*="language-"] {
90 | padding: 0.1em;
91 | border-radius: 0.3em;
92 | white-space: normal;
93 | }
94 |
95 | .token.comment,
96 | .token.prolog,
97 | .token.doctype,
98 | .token.cdata {
99 | color: #8292a2;
100 | }
101 |
102 | .token.punctuation {
103 | color: #f8f8f2;
104 | }
105 |
106 | .token.namespace {
107 | opacity: 0.7;
108 | }
109 |
110 | .token.property,
111 | .token.tag,
112 | .token.constant,
113 | .token.symbol,
114 | .token.deleted {
115 | color: #f92672;
116 | }
117 |
118 | .token.boolean,
119 | .token.number {
120 | color: #ae81ff;
121 | }
122 |
123 | .token.selector,
124 | .token.attr-name,
125 | .token.string,
126 | .token.char,
127 | .token.builtin,
128 | .token.inserted {
129 | color: #a6e22e;
130 | }
131 |
132 | .token.operator,
133 | .token.entity,
134 | .token.url,
135 | .language-css .token.string,
136 | .style .token.string,
137 | .token.variable {
138 | color: #f8f8f2;
139 | }
140 |
141 | .token.atrule,
142 | .token.attr-value,
143 | .token.function,
144 | .token.class-name {
145 | color: #e6db74;
146 | }
147 |
148 | .token.keyword {
149 | color: #66d9ef;
150 | }
151 |
152 | .token.regex,
153 | .token.important {
154 | color: #fd971f;
155 | }
156 |
157 | .token.important,
158 | .token.bold {
159 | font-weight: bold;
160 | }
161 | .token.italic {
162 | font-style: italic;
163 | }
164 |
165 | .token.entity {
166 | cursor: help;
167 | }
168 | `;
169 |
--------------------------------------------------------------------------------
/astro/src/components/CodeBlock/code-block.ts:
--------------------------------------------------------------------------------
1 | import { html, LitElement } from "lit";
2 | import { customElement, state, property } from "lit/decorators.js";
3 | import { codeBlockStyles} from './code-block-styles';
4 | import Prism from "prismjs";
5 | import "prismjs/components/prism-bash.js";
6 |
7 | @customElement("code-block")
8 | export class CodeBlock extends LitElement {
9 | static styles = [codeBlockStyles];
10 |
11 | @property({ type: String, attribute: "code" })
12 | code = "";
13 |
14 | @property({ type: String, attribute: "language" })
15 | language = "";
16 |
17 | @state()
18 | isCopied = false;
19 |
20 | copyCode() {
21 | this.isCopied = true;
22 | const codeElement = this.shadowRoot.querySelector("code");
23 | const range = document.createRange();
24 | range.selectNode(codeElement);
25 | window.getSelection().removeAllRanges();
26 | window.getSelection().addRange(range);
27 | // check if the browser supports clipboard API
28 | if (!navigator.clipboard) {
29 | // if not use the old execCommand() way
30 | document.execCommand('copy');
31 | } else {
32 | try {
33 | navigator.clipboard.writeText(range.toString());
34 | } catch (error) {
35 | console.error(error);
36 | }
37 | }
38 | window.getSelection().removeAllRanges();
39 | // Set a timeout to reset the button
40 | setTimeout(() => {
41 | this.isCopied = false;
42 | }, 1000);
43 | }
44 |
45 | firstUpdated() {
46 | const highlight = Prism.highlight(
47 | this.code,
48 | Prism.languages[this.language],
49 | this.language
50 | );
51 | // Set to our styled block
52 | this.shadowRoot.querySelector("#output").innerHTML = highlight;
53 | }
54 |
55 | render() {
56 | return html`
57 |
58 |
59 | ${this.isCopied ? "Copied! 🎉" : "Copy"}
60 |
61 |
62 | ${this.language}
63 |
64 |
65 |
66 | `;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/astro/src/components/Container.astro:
--------------------------------------------------------------------------------
1 | ---
2 | export interface Props {
3 | narrow?: boolean;
4 | }
5 |
6 | const {narrow} = Astro.props;
7 | ---
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/astro/src/components/SanityPortableText.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { sanityPortableText } from "../lib/sanityPortableText";
3 |
4 | const {portableText} = Astro.props;
5 | ---
6 |
7 |
--------------------------------------------------------------------------------
/astro/src/data/settings.js:
--------------------------------------------------------------------------------
1 | export default {
2 | title: `Astro Sanity Minimal Starter`,
3 | description: `This is an example blog made with Astro.`,
4 | url: `https://astro-sanity-minimal-starter.netlify.app`, // No trailing slash!
5 | // JSON LD
6 | name: `Example`,
7 | // Facebook URL
8 | facebookURL: `https://facebook.com`,
9 | // Twitter URL
10 | twitterURL: `https://twitter.com/jaydanurwin`,
11 | // Instagram URL
12 | instagramURL: `https://instagram.com/jaydanurwin`,
13 | }
--------------------------------------------------------------------------------
/astro/src/layouts/Base.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import BaseHead from "../components/BaseHead.astro";
3 | import type { Props as BaseHeadProps } from '../components/BaseHead.astro';
4 |
5 |
6 | export interface Props {
7 | seo?: BaseHeadProps;
8 | }
9 |
10 | const { seo } = Astro.props;
11 | ---
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/astro/src/layouts/Page.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from './Base.astro';
3 | import type { Props as BaseHeadProps } from '../components/BaseHead.astro';
4 | // import Header from '../components/Header.astro';
5 | // import Footer from '../components/Footer.astro';
6 |
7 | export interface Props {
8 | seo?: BaseHeadProps;
9 | }
10 |
11 | const { seo } = Astro.props as Props;
12 | ---
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/astro/src/layouts/Post.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from '../layouts/Page.astro'
3 | import type { Props as BaseHeadProps } from '../components/BaseHead.astro';
4 | import ArticleSchema from '../components/ArticleSchema.astro';
5 |
6 | export interface Props {
7 | seo?: BaseHeadProps;
8 | }
9 |
10 | const { seo } = Astro.props as Props;
11 | ---
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/astro/src/lib/api.js:
--------------------------------------------------------------------------------
1 | import { useSanityClient } from "astro-sanity";
2 |
3 | export async function getAllPosts() {
4 | const query = `*[_type == 'post']{"categoryData": categories[]->{slug, title},author -> {name}, ...} | order(publishedAt desc)`;
5 | const data = await useSanityClient().fetch(query);
6 | return data;
7 | }
8 |
9 | export async function getAllCategoriesWithPosts() {
10 | const query = `*[_type == 'category']{"posts": *[_type == "post" && references(^._id)] | order(publishedAt desc), ...}`;
11 | const data = await useSanityClient().fetch(query);
12 | return data;
13 | }
--------------------------------------------------------------------------------
/astro/src/lib/sanityPortableText.js:
--------------------------------------------------------------------------------
1 | import { portableTextToHtml } from 'astro-sanity';
2 | import { getSanityImageURL } from '../utils/helpers.js';
3 |
4 | const customComponents = {
5 | types: {
6 | mainImage: ({ value }) => {
7 | return `
8 |
9 |
13 |
18 |
19 | `;
20 | },
21 | image: ({ value }) => {
22 | return `
23 |
24 |
28 |
33 |
34 | `;
35 | },
36 | code: ({ value }) => {
37 | return ` `;
38 | },
39 | },
40 | };
41 |
42 | export function sanityPortableText(portabletext) {
43 | return portableTextToHtml(portabletext, customComponents);
44 | }
--------------------------------------------------------------------------------
/astro/src/pages/404.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from '../layouts/Page.astro';
3 | import Container from '../components/Container.astro';
4 |
5 | const seo = {
6 | title: 'Uh Oh! Page Not Found',
7 | description: 'This is a starter repo for an blog built with Astro including open graph tags, canonical urls, RSS, sitemap support.',
8 | }
9 |
10 | ---
11 |
12 |
13 |
14 | 404
15 | Uh Oh! We couldn't find the page you were looking for
16 | Return Home
17 |
18 |
--------------------------------------------------------------------------------
/astro/src/pages/blog/[slug].astro:
--------------------------------------------------------------------------------
1 | ---
2 | // Component Imports
3 | import Layout from '../../layouts/Post.astro'
4 | import BlogPost from '../../components/BlogPost.astro'
5 | import { getSanityImageURL } from '../../utils/helpers.js';
6 | import { getAllPosts } from '../../lib/api.js';
7 |
8 | export async function getStaticPaths() {
9 | const allBlogPosts = await getAllPosts();
10 |
11 | // rss({
12 | // title: 'Example Blog',
13 | // description: 'An example blog on Astro',
14 | // customData: `en-us `,
15 | // items: allBlogPosts.map(item => ({
16 | // title: item.title,
17 | // description: item.description,
18 | // link: `/blog/${item.slug.current}`,
19 | // pubDate: item.publishedAt,
20 | // })),
21 | // });
22 | return allBlogPosts.map(post => ({ params: { slug: post.slug.current }, props: { post } }));
23 | }
24 |
25 | const { post } = Astro.props;
26 |
27 | const seo = {
28 | title: post.title,
29 | description: post.description,
30 | image: getSanityImageURL(post.mainImage).width(1200).url(),
31 | }
32 | ---
33 |
34 |
35 | {post.categoryData && }
40 |
41 |
--------------------------------------------------------------------------------
/astro/src/pages/blog/category/[slug].astro:
--------------------------------------------------------------------------------
1 | ---
2 | // Component Imports
3 | import Layout from '../../../layouts/Page.astro'
4 | import BlogPostPreview from '../../../components/BlogPostPreview.astro'
5 |
6 | import { getAllCategoriesWithPosts } from '../../../lib/api.js';
7 |
8 | export async function getStaticPaths() {
9 | const allCategoryData = await getAllCategoriesWithPosts();
10 |
11 | return allCategoryData.map(category => ({params: { slug: category.slug.current }, props: {category}}));
12 | }
13 |
14 | const { category } = Astro.props;
15 |
16 | const seo = {
17 | title: category.title,
18 | }
19 | ---
20 |
21 |
22 | All Posts Tagged with "{category.title}"
23 | {category.posts.map((post) => (
24 |
25 | ))}
26 |
--------------------------------------------------------------------------------
/astro/src/pages/blog/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | // Component Imports
3 | import Layout from '../../layouts/Page.astro'
4 | import BlogPostPreview from '../../components/BlogPostPreview.astro';
5 |
6 | import { getAllPosts } from '../../lib/api.js';
7 | import Container from '../../components/Container.astro';
8 |
9 |
10 | const seo = {
11 | title: 'Example Blog',
12 | description: 'This is a starter repo for an blog built with Astro including open graph tags, canonical urls, RSS, sitemap support.'
13 | }
14 |
15 | const allPostData = await getAllPosts();
16 |
17 | ---
18 |
19 |
20 |
21 |
22 |
23 | {allPostData.map((post) =>
24 | )}
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/astro/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from '../layouts/Page.astro';
3 | import Container from '../components/Container.astro';
4 |
5 |
6 | const seo = {
7 | title: "Astro Minimal Starter",
8 | description: "This is a starter repo for an blog built with Astro including open graph tags, canonical urls, RSS, sitemap support."
9 | }
10 | ---
11 |
12 |
13 |
14 | Astro Sanity Minimal Starter
15 |
16 |
17 | If you haven't heard about Astro yet you're missing out. This starter repo gives you a blog website with a headless CMS setup using Sanity.io .
18 |
19 |
20 | Don't be fooled by the simplicity of this starter either. With excellent SEO built in, Astro's excellent developer environment, and performance best practices you're getting one of the best foundations for a personal blog with posts written in markdown. The reason for making such a bare bones starter in terms of styles is that it has what you need out of the box for SEO and site structure but it makes no assumptions about how you want it look and behave.
21 |
22 |
23 | Sometimes starters can feel like they are giving us way too much and you'll spend a day picking apart half of the code to get to the setup you actually wanted. Want to use vanilla CSS instead of Tailwind or Scss? No problem!
24 |
25 | Features
26 |
27 | A customizable headless CMS with Sanity.io
28 | Excellent SEO Built In
29 | Open Graph Tags (Twitter, Facebook, etc.)
30 | User Declared Canonical URLs
31 | RSS Feed
32 | XML Sitemap
33 | Robots.txt
34 | JSON LD Schema
35 |
36 |
37 | Blog →
38 |
39 |
--------------------------------------------------------------------------------
/astro/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @import url('./reset.css');
2 |
3 | :root {
4 | --container-max-width: 1200px;
5 | --container-max-width-narrow: 960px;
6 | --container-padding: 0 1rem;
7 | }
8 |
9 | html,
10 | body {
11 | margin: 0;
12 | padding: 0;
13 | background: #fff;
14 | color: #000;
15 | font-family: 'Open Sans', Arial, Helvetica, sans-serif;
16 | }
17 |
18 | p {
19 | margin-bottom: 1rem;
20 | }
21 |
--------------------------------------------------------------------------------
/astro/src/styles/reset.css:
--------------------------------------------------------------------------------
1 | /* Modern CSS Reset from https://piccalil.li/blog/a-modern-css-reset/ */
2 |
3 | /* Box sizing rules */
4 | *,
5 | *::before,
6 | *::after {
7 | box-sizing: border-box;
8 | }
9 |
10 | /* Remove default margin */
11 | body,
12 | h1,
13 | h2,
14 | h3,
15 | h4,
16 | p,
17 | figure,
18 | blockquote,
19 | dl,
20 | dd {
21 | margin: 0;
22 | }
23 |
24 | /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
25 | ul[role='list'],
26 | ol[role='list'] {
27 | list-style: none;
28 | }
29 |
30 | /* Set core root defaults */
31 | html:focus-within {
32 | scroll-behavior: smooth;
33 | }
34 |
35 | /* Set core body defaults */
36 | body {
37 | min-height: 100vh;
38 | text-rendering: optimizeSpeed;
39 | line-height: 1.5;
40 | }
41 |
42 | /* A elements that don't have a class get default styles */
43 | a:not([class]) {
44 | text-decoration-skip-ink: auto;
45 | }
46 |
47 | /* Make images easier to work with */
48 | img,
49 | picture {
50 | max-width: 100%;
51 | display: block;
52 | }
53 |
54 | /* Inherit fonts for inputs and buttons */
55 | input,
56 | button,
57 | textarea,
58 | select {
59 | font: inherit;
60 | }
61 |
62 | /* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
63 | @media (prefers-reduced-motion: reduce) {
64 | html:focus-within {
65 | scroll-behavior: auto;
66 | }
67 |
68 | *,
69 | *::before,
70 | *::after {
71 | animation-duration: 0.01ms !important;
72 | animation-iteration-count: 1 !important;
73 | transition-duration: 0.01ms !important;
74 | scroll-behavior: auto !important;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/astro/src/utils/helpers.js:
--------------------------------------------------------------------------------
1 | import { parseISO, format } from 'date-fns';
2 | import { useSanityClient, createImageBuilder} from 'astro-sanity';
3 |
4 | const builder = createImageBuilder(useSanityClient());
5 |
6 | export function formatBlogPostDate(date) {
7 | const dateString = parseISO(date, 'YYYY/MM/Do');
8 | const formattedDateString = format(dateString, 'MMMM do, yyyy');
9 | return `${formattedDateString}`;
10 | }
11 |
12 | export function getSanityImageURL(source) {
13 | return builder.image(source);
14 | }
--------------------------------------------------------------------------------
/astro/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "experimentalDecorators": true,
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "astro-sanity-minimal-starter",
3 | "version": "1.1.1",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "astro-sanity-minimal-starter",
9 | "version": "1.1.1"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": false,
3 | "name": "astro-sanity-minimal-starter",
4 | "version": "3.1.0",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/sanity/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /node_modules
3 |
4 | yarn.lock
--------------------------------------------------------------------------------
/sanity/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | /logs
3 | *.log
4 |
5 | # Coverage directory used by tools like istanbul
6 | /coverage
7 |
8 | # Dependency directories
9 | node_modules
10 |
11 | # Compiled sanity studio
12 | /dist
13 |
--------------------------------------------------------------------------------
/sanity/README.md:
--------------------------------------------------------------------------------
1 | # Sanity Blogging Content Studio
2 |
3 | Congratulations, you have now installed the Sanity Content Studio, an open source real-time content editing environment connected to the Sanity backend.
4 |
5 | Now you can do the following things:
6 |
7 | - [Read “getting started” in the docs](https://www.sanity.io/docs/introduction/getting-started?utm_source=readme)
8 | - Check out the example frontend: [React/Next.js](https://github.com/sanity-io/tutorial-sanity-blog-react-next)
9 | - [Read the blog post about this template](https://www.sanity.io/blog/build-your-own-blog-with-sanity-and-next-js?utm_source=readme)
10 | - [Join the community Slack](https://slack.sanity.io/?utm_source=readme)
11 | - [Extend and build plugins](https://www.sanity.io/docs/content-studio/extending?utm_source=readme)
12 |
--------------------------------------------------------------------------------
/sanity/config/.checksums:
--------------------------------------------------------------------------------
1 | {
2 | "#": "Used by Sanity to keep track of configuration file checksums, do not delete or modify!",
3 | "@sanity/default-layout": "bb034f391ba508a6ca8cd971967cbedeb131c4d19b17b28a0895f32db5d568ea",
4 | "@sanity/default-login": "6fb6d3800aa71346e1b84d95bbcaa287879456f2922372bb0294e30b968cd37f",
5 | "@sanity/form-builder": "b38478227ba5e22c91981da4b53436df22e48ff25238a55a973ed620be5068aa",
6 | "@sanity/data-aspects": "d199e2c199b3e26cd28b68dc84d7fc01c9186bf5089580f2e2446994d36b3cb6",
7 | "@sanity/vision": "da5b6ed712703ecd04bf4df560570c668aa95252c6bc1c41d6df1bda9b8b8f60"
8 | }
9 |
--------------------------------------------------------------------------------
/sanity/config/@sanity/data-aspects.json:
--------------------------------------------------------------------------------
1 | {
2 | "listOptions": {}
3 | }
4 |
--------------------------------------------------------------------------------
/sanity/config/@sanity/default-layout.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolSwitcher": {
3 | "order": [],
4 | "hidden": []
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/sanity/config/@sanity/default-login.json:
--------------------------------------------------------------------------------
1 | {
2 | "providers": {
3 | "mode": "append",
4 | "redirectOnSingle": false,
5 | "entries": []
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/sanity/config/@sanity/form-builder.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": {
3 | "directUploads": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/sanity/config/@sanity/vision.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultApiVersion": "2021-10-21"
3 | }
4 |
--------------------------------------------------------------------------------
/sanity/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sanityminimalstarter",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "",
6 | "main": "package.json",
7 | "author": "Jaydan Urwin ",
8 | "license": "UNLICENSED",
9 | "scripts": {
10 | "start": "sanity start",
11 | "build": "sanity build",
12 | "graphql-deploy": "sanity graphql deploy --playground"
13 | },
14 | "keywords": [
15 | "sanity"
16 | ],
17 | "dependencies": {
18 | "@sanity/base": "^2.30.0",
19 | "@sanity/code-input": "^2.30.0",
20 | "@sanity/core": "^2.30.0",
21 | "@sanity/default-layout": "^2.30.0",
22 | "@sanity/default-login": "^2.30.0",
23 | "@sanity/desk-tool": "^2.30.0",
24 | "@sanity/vision": "^2.30.0",
25 | "prop-types": "^15.7",
26 | "react": "^17.0",
27 | "react-dom": "^17.0",
28 | "styled-components": "^5.2.0"
29 | },
30 | "devDependencies": {}
31 | }
32 |
--------------------------------------------------------------------------------
/sanity/plugins/.gitkeep:
--------------------------------------------------------------------------------
1 | User-specific packages can be placed here
2 |
--------------------------------------------------------------------------------
/sanity/sanity.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "project": {
4 | "name": "Sanity Minimal Starter"
5 | },
6 | "api": {
7 | "projectId": "8hj1t7km",
8 | "dataset": "production"
9 | },
10 | "plugins": [
11 | "@sanity/base",
12 | "@sanity/default-layout",
13 | "@sanity/default-login",
14 | "@sanity/desk-tool",
15 | "@sanity/code-input"
16 | ],
17 | "env": {
18 | "development": {
19 | "plugins": [
20 | "@sanity/vision"
21 | ]
22 | }
23 | },
24 | "parts": [
25 | {
26 | "name": "part:@sanity/base/schema",
27 | "path": "./schemas/schema"
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/sanity/schemas/author.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'author',
3 | title: 'Author',
4 | type: 'document',
5 | fields: [
6 | {
7 | name: 'name',
8 | title: 'Name',
9 | type: 'string',
10 | },
11 | {
12 | name: 'slug',
13 | title: 'Slug',
14 | type: 'slug',
15 | options: {
16 | source: 'name',
17 | maxLength: 96,
18 | },
19 | },
20 | {
21 | name: 'image',
22 | title: 'Image',
23 | type: 'image',
24 | options: {
25 | hotspot: true,
26 | },
27 | },
28 | {
29 | name: 'bio',
30 | title: 'Bio',
31 | type: 'array',
32 | of: [
33 | {
34 | title: 'Block',
35 | type: 'block',
36 | styles: [{title: 'Normal', value: 'normal'}],
37 | lists: [],
38 | },
39 | ],
40 | },
41 | ],
42 | preview: {
43 | select: {
44 | title: 'name',
45 | media: 'image',
46 | },
47 | },
48 | }
49 |
--------------------------------------------------------------------------------
/sanity/schemas/blockContent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is the schema definition for the rich text fields used for
3 | * for this blog studio. When you import it in schemas.js it can be
4 | * reused in other parts of the studio with:
5 | * {
6 | * name: 'someName',
7 | * title: 'Some title',
8 | * type: 'blockContent'
9 | * }
10 | */
11 | export default {
12 | title: 'Block Content',
13 | name: 'blockContent',
14 | type: 'array',
15 | of: [
16 | {
17 | title: 'Block',
18 | type: 'block',
19 | // Styles let you set what your user can mark up blocks with. These
20 | // correspond with HTML tags, but you can set any title or value
21 | // you want and decide how you want to deal with it where you want to
22 | // use your content.
23 | styles: [
24 | {title: 'Normal', value: 'normal'},
25 | {title: 'H1', value: 'h1'},
26 | {title: 'H2', value: 'h2'},
27 | {title: 'H3', value: 'h3'},
28 | {title: 'H4', value: 'h4'},
29 | {title: 'Quote', value: 'blockquote'},
30 | ],
31 | lists: [{title: 'Bullet', value: 'bullet'}],
32 | // Marks let you mark up inline text in the block editor.
33 | marks: {
34 | // Decorators usually describe a single property – e.g. a typographic
35 | // preference or highlighting by editors.
36 | decorators: [
37 | {title: 'Strong', value: 'strong'},
38 | {title: 'Emphasis', value: 'em'},
39 | ],
40 | // Annotations can be any object structure – e.g. a link or a footnote.
41 | annotations: [
42 | {
43 | title: 'URL',
44 | name: 'link',
45 | type: 'object',
46 | fields: [
47 | {
48 | title: 'URL',
49 | name: 'href',
50 | type: 'url',
51 | },
52 | ],
53 | },
54 | ],
55 | },
56 | },
57 | // You can add additional types here. Note that you can't use
58 | // primitive types such as 'string' and 'number' in the same array
59 | // as a block type.
60 | {
61 | type: 'image',
62 | options: {hotspot: true},
63 | },
64 | {
65 | type: 'code'
66 | }
67 | ],
68 | }
69 |
--------------------------------------------------------------------------------
/sanity/schemas/category.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'category',
3 | title: 'Category',
4 | type: 'document',
5 | fields: [
6 | {
7 | name: 'title',
8 | title: 'Title',
9 | type: 'string',
10 | },
11 | {
12 | name: 'slug',
13 | title: 'Slug',
14 | type: 'slug',
15 | options: {
16 | source: 'title',
17 | maxLength: 96,
18 | },
19 | },
20 | {
21 | name: 'description',
22 | title: 'Description',
23 | type: 'text',
24 | },
25 | ],
26 | }
--------------------------------------------------------------------------------
/sanity/schemas/post.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'post',
3 | title: 'Post',
4 | type: 'document',
5 | fields: [
6 | {
7 | name: 'title',
8 | title: 'Title',
9 | type: 'string',
10 | },
11 | {
12 | name: 'slug',
13 | title: 'Slug',
14 | type: 'slug',
15 | options: {
16 | source: 'title',
17 | maxLength: 96,
18 | },
19 | },
20 | {
21 | name: 'author',
22 | title: 'Author',
23 | type: 'reference',
24 | to: {type: 'author'},
25 | },
26 | {
27 | name: 'mainImage',
28 | title: 'Main image',
29 | type: 'image',
30 | options: {
31 | hotspot: true,
32 | },
33 | },
34 | {
35 | name: 'categories',
36 | title: 'Categories',
37 | type: 'array',
38 | of: [{type: 'reference', to: {type: 'category'}}],
39 | },
40 | {
41 | name: 'publishedAt',
42 | title: 'Published at',
43 | type: 'datetime',
44 | },
45 | {
46 | name: 'excerpt',
47 | title: 'Excerpt',
48 | type: 'text',
49 | validation: Rule => Rule.max(200).warning('The description shouldn\'t be longer than 200 characters'),
50 | },
51 | {
52 | name: 'body',
53 | title: 'Body',
54 | type: 'blockContent',
55 | },
56 | ],
57 |
58 | preview: {
59 | select: {
60 | title: 'title',
61 | author: 'author.name',
62 | media: 'mainImage',
63 | },
64 | prepare(selection) {
65 | const {author} = selection
66 | return Object.assign({}, selection, {
67 | subtitle: author && `by ${author}`,
68 | })
69 | },
70 | },
71 | }
72 |
--------------------------------------------------------------------------------
/sanity/schemas/schema.js:
--------------------------------------------------------------------------------
1 | // First, we must import the schema creator
2 | import createSchema from 'part:@sanity/base/schema-creator'
3 |
4 | // Then import schema types from any plugins that might expose them
5 | import schemaTypes from 'all:part:@sanity/base/schema-type'
6 |
7 | // We import object and document schemas
8 | import blockContent from './blockContent'
9 | import category from './category'
10 | import post from './post'
11 | import author from './author'
12 |
13 | // Then we give our schema to the builder and provide the result to Sanity
14 | export default createSchema({
15 | // We name our schema
16 | name: 'default',
17 | // Then proceed to concatenate our document type
18 | // to the ones provided by any plugins that are installed
19 | types: schemaTypes.concat([
20 | // The following are document types which will appear
21 | // in the studio.
22 | post,
23 | author,
24 | category,
25 | // When added to this list, object types can be used as
26 | // { type: 'typename' } in other document schemas
27 | blockContent,
28 | ]),
29 | })
30 |
--------------------------------------------------------------------------------
/sanity/static/.gitkeep:
--------------------------------------------------------------------------------
1 | Files placed here will be served by the Sanity server under the `/static`-prefix
2 |
--------------------------------------------------------------------------------
/sanity/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/treefarmstudio/astro-sanity-minimal-starter/1e89323443682f6a6d571f37f8fc50a5c901f92c/sanity/static/favicon.ico
--------------------------------------------------------------------------------
/sanity/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // Note: This config is only used to help editors like VS Code understand/resolve
3 | // parts, the actual transpilation is done by babel. Any compiler configuration in
4 | // here will be ignored.
5 | "include": ["./node_modules/@sanity/base/types/**/*.ts", "./**/*.ts", "./**/*.tsx"]
6 | }
7 |
--------------------------------------------------------------------------------