Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
131 changes: 46 additions & 85 deletions app/routes/_libraries/blog.index.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,25 @@
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
)

if (!file) {
throw notFound()
}

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
}
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 (
new Date(b[1].published || 0).getTime() -
new Date(a[1].published || 0).getTime()
)
return allPosts.sort((a, b) => {
return new Date(b.published).getTime() - new Date(a.published).getTime()
})

// return json(frontMatters, {
Expand Down Expand Up @@ -93,50 +55,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
Loading