Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ yarn.lock
dist
.vscode/
.env.local

# Content Collections generated files
.content-collections

2 changes: 2 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { sentryVitePlugin } from '@sentry/vite-plugin'
import { defineConfig } from '@tanstack/start/config'
import contentCollections from '@content-collections/vite'
import tsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
Expand Down Expand Up @@ -45,6 +46,7 @@ export default defineConfig({
org: 'tanstack',
project: 'tanstack-com',
}),
contentCollections(),
],
},
},
Expand Down
2 changes: 1 addition & 1 deletion app/blog/ag-grid-partnership.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: TanStack Table + Ag-Grid Partnership
published: 6/17/2022
published: 2022-06-17
authors:
- Tanner Linsley
- Niall Crosby
Expand Down
2 changes: 1 addition & 1 deletion app/blog/announcing-tanstack-form-v1.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Announcing TanStack Form v1
published: 03/03/2025
published: 2025-03-03
authors:
- Corbin Crutchley
---
Expand Down
2 changes: 1 addition & 1 deletion app/blog/announcing-tanstack-query-v4.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Announcing TanStack Query v4
published: 7/14/2022
published: 2022-07-14
authors:
- Dominik Dorfmeister
---
Expand Down
2 changes: 1 addition & 1 deletion app/blog/announcing-tanstack-query-v5.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Announcing TanStack Query v5
published: 10/17/2023
published: 2023-10-17
authors:
- Dominik Dorfmeister
---
Expand Down
2 changes: 1 addition & 1 deletion app/blog/netlify-partnership.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: TanStack + Netlify Partnership
published: 3/18/2025
published: 2025-03-18
authors:
- Tanner Linsley
---
Expand Down
2 changes: 1 addition & 1 deletion app/blog/tanstack-router-typescript-performance.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: A milestone for TypeScript Performance in TanStack Router
published: 09/17/2024
published: 2024-09-17
authors:
- Christopher Horobin
---
Expand Down
2 changes: 1 addition & 1 deletion app/blog/why-tanstack-start-and-router.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Why choose TanStack Start and Router?
published: 12/03/2024
published: 2024-12-03
authors:
- Tanner Linsley
---
Expand Down
2 changes: 1 addition & 1 deletion app/blog/why-tanstack-start-is-ditching-adapters.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Why TanStack Start is Ditching Adapters
published: 11/22/2024
published: 2024-11-22
authors:
- Tanner Linsley
---
Expand Down
20 changes: 8 additions & 12 deletions app/routes/_libraries/blog.$.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { createFileRoute, Link, notFound } from '@tanstack/react-router'
import { extractFrontMatter, fetchRepoFile } from '~/utils/documents.server'
import removeMarkdown from 'remove-markdown'
import { seo } from '~/utils/seo'
import { Doc } from '~/components/Doc'
import { PostNotFound } from './blog'
Expand All @@ -11,6 +9,7 @@ import { z } from 'zod'
import { FaArrowLeft } from 'react-icons/fa'
import { DocContainer } from '~/components/DocContainer'
import { setHeaders } from 'vinxi/http'
import { allPosts } from 'content-collections'

const fetchBlogPost = createServerFn({ method: 'GET' })
.validator(z.string().optional())
Expand All @@ -21,27 +20,24 @@ const fetchBlogPost = createServerFn({ method: 'GET' })

const filePath = `app/blog/${docsPath}.md`

const file = await fetchRepoFile('tanstack/tanstack.com', 'main', filePath)
const post = allPosts.find((post) => post.slug === docsPath)

if (!file) {
if (!post) {
throw notFound()
}

const frontMatter = extractFrontMatter(file)
const description = removeMarkdown(frontMatter.excerpt ?? '')

setHeaders({
'cache-control': 'public, max-age=0, must-revalidate',
'cdn-cache-control': 'max-age=300, stale-while-revalidate=300, durable',
'Netlify-Vary': 'query=payload',
})

return {
title: frontMatter.data.title,
description,
published: frontMatter.data.published,
content: frontMatter.content,
authors: (frontMatter.data.authors ?? []) as Array<string>,
title: post.title,
description: post.excerpt,
published: post.published,
content: post.content,
authors: post.authors,
filePath,
}
})
Expand Down
139 changes: 55 additions & 84 deletions app/routes/_libraries/blog.index.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,36 @@
import { Link, createFileRoute, notFound } from '@tanstack/react-router'
import { Link, createFileRoute } from '@tanstack/react-router'

import { formatAuthors, getPostList } from '~/utils/blog'
import { formatAuthors } from '~/utils/blog'
import { DocTitle } from '~/components/DocTitle'
import { Markdown } from '~/components/Markdown'
import { format } from 'date-fns'
import { Footer } from '~/components/Footer'
import { extractFrontMatter, fetchRepoFile } from '~/utils/documents.server'
import { PostNotFound } from './blog'
import { createServerFn } from '@tanstack/start'
import { allPosts } from 'content-collections'
import { setHeaders } from 'vinxi/http'

const fetchFrontMatters = createServerFn({ method: 'GET' }).handler(
async () => {
const postInfos = getPostList()

const frontMatters = await Promise.all(
postInfos.map(async (info) => {
const filePath = `app/blog/${info.id}.md`

const file = await fetchRepoFile(
'tanstack/tanstack.com',
'main',
filePath
)
setHeaders({
'cache-control': 'public, max-age=0, must-revalidate',
'cdn-cache-control': 'max-age=300, stale-while-revalidate=300, durable',
'Netlify-Vary': 'query=payload',
})

if (!file) {
throw notFound()
return allPosts
.sort((a, b) => {
return new Date(b.published).getTime() - new Date(a.published).getTime()
})
.map((post) => {
return {
slug: post.slug,
title: post.title,
published: post.published,
excerpt: post.excerpt,
authors: post.authors,
}

const frontMatter = extractFrontMatter(file)

setHeaders({
'cache-control': 'public, max-age=0, must-revalidate',
'cdn-cache-control':
'max-age=300, stale-while-revalidate=300, durable',
'Netlify-Vary': 'query=payload',
})

return [
info.id,
{
title: frontMatter.data.title,
published: frontMatter.data.published,
excerpt: frontMatter.excerpt,
authors: frontMatter.data.authors as Array<string> | undefined,
},
] as const
})
)

return frontMatters.sort((a, b) => {
if (!a[1].published) {
return 1
}

return (
new Date(b[1].published || 0).getTime() -
new Date(a[1].published || 0).getTime()
)
})

// return json(frontMatters, {
// headers: {
Expand Down Expand Up @@ -93,50 +65,49 @@ function BlogIndex() {
<div className="h-6" />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-3 gap-4">
{frontMatters.map(
([id, { title, published, excerpt, authors = [] }]) => {
return (
<Link
key={id}
to={`${id}`}
className={`flex flex-col gap-4 justify-between
{frontMatters.map(({ slug, title, published, excerpt, authors }) => {
return (
<Link
key={slug}
to="/blog/$"
params={{ _splat: slug }}
className={`flex flex-col gap-4 justify-between
border-2 border-transparent rounded-lg p-4 md:p-8
transition-all bg-white/100 dark:bg-gray-800
shadow-xl dark:shadow-lg dark:shadow-blue-500/30
hover:border-blue-500
`}
>
<div>
<div className={`text-lg font-extrabold`}>{title}</div>
<div className={`text-xs italic font-light mt-1`}>
<p>
by {formatAuthors(authors)}
{published ? (
<time
dateTime={published}
title={format(new Date(published), 'MMM dd, yyyy')}
>
{' '}
on {format(new Date(published), 'MMM dd, yyyy')}
</time>
) : null}
</p>
</div>
<div
className={`text-sm mt-4 text-black dark:text-white leading-7`}
>
<Markdown rawContent={excerpt || ''} />
</div>
>
<div>
<div className={`text-lg font-extrabold`}>{title}</div>
<div className={`text-xs italic font-light mt-1`}>
<p>
by {formatAuthors(authors)}
{published ? (
<time
dateTime={published}
title={format(new Date(published), 'MMM dd, yyyy')}
>
{' '}
on {format(new Date(published), 'MMM dd, yyyy')}
</time>
) : null}
</p>
</div>
<div
className={`text-sm mt-4 text-black dark:text-white leading-7`}
>
<Markdown rawContent={excerpt || ''} />
</div>
<div>
<div className="text-blue-500 uppercase font-black text-sm">
Read More
</div>
</div>
<div>
<div className="text-blue-500 uppercase font-black text-sm">
Read More
</div>
</Link>
)
}
)}
</div>
</Link>
)
})}
</div>
<div className="h-24" />
</div>
Expand Down
42 changes: 5 additions & 37 deletions app/utils/blog.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,12 @@
export function getPostList() {
return [
{
id: 'announcing-tanstack-form-v1',
},
{
id: 'announcing-tanstack-query-v5',
},
{
id: 'announcing-tanstack-query-v4',
},
{
id: 'ag-grid-partnership',
},
{
id: 'tanstack-router-typescript-performance',
},
{
id: 'why-tanstack-start-is-ditching-adapters',
},
{
id: 'why-tanstack-start-and-router',
},
{
id: 'netlify-partnership',
},
]
}
const listJoiner = new Intl.ListFormat('en-US', {
style: 'long',
type: 'conjunction',
})

export function formatAuthors(authors: Array<string>) {
if (!authors.length) {
return 'TanStack'
}

if (authors.length === 1) {
return authors[0]
}

if (authors.length === 2) {
return authors.join(' and ')
}

return authors.slice(0, -1).join(', ') + ', and ' + authors.slice(-1)
return listJoiner.format(authors)
}
26 changes: 26 additions & 0 deletions content-collections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineCollection, defineConfig } from '@content-collections/core'
import { extractFrontMatter } from '~/utils/documents.server'

const posts = defineCollection({
name: 'posts',
directory: './app/blog',
include: '*.md',
schema: (z) => ({
title: z.string(),
published: z.string().date(),
authors: z.string().array(),
}),
transform: ({ content, ...post }) => {
const frontMatter = extractFrontMatter(content)
return {
...post,
slug: post._meta.path,
excerpt: frontMatter.excerpt,
content,
}
},
})

export default defineConfig({
collections: [posts],
})
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
"zustand": "^4.5.2"
},
"devDependencies": {
"@content-collections/core": "^0.8.2",
"@content-collections/vite": "^0.2.4",
"@shikijs/transformers": "^1.10.3",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
Expand Down
Loading