Skip to content

Commit

Permalink
Merge branch 'main' into upgradeOramaSearchbox
Browse files Browse the repository at this point in the history
  • Loading branch information
rjborba authored Dec 4, 2024
2 parents 3d5fbc2 + bf7c65c commit b80d36a
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 66 deletions.
74 changes: 74 additions & 0 deletions app/blog/why-tanstack-start-and-router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: Why choose TanStack Start and Router?
published: 12/03/2024
authors:
- Tanner Linsley
---

![TanStack Start and Router Blog Header](/blog-assets/why-tanstack-start-and-router/tanstack-start-blog-header.jpg)

Building modern web applications is no small feat. The frameworks and tools we choose can make or break not only our developer experience but also the success of the applications we build. While there are many great frameworks out there, I believe **TanStack Router** and **TanStack Start** stand apart for their ability to solve the challenges developers face today and their readiness for what’s coming tomorrow.

These aren’t just another set of tools—they represent a commitment to building better apps with less friction and more joy. Here’s why I think you’ll love working with them as much as I do.

### Type Safety You Can Rely On

Type safety isn’t just a buzzword—it’s a foundational tool for creating robust, maintainable applications. TanStack Router goes beyond the basics to offer **contextual type safety**, where types flow seamlessly through every part of your app. Route definitions, parameters, navigation, and even state management all work together with fully inferred types.

What does this mean for you? It means no more guessing if you’ve defined a parameter correctly, no more debugging mismatched types, and no need for extra plugins or AST transformations to fill in the gaps. TanStack Router works with TypeScript’s natural architecture, making the experience smooth, predictable, and delightful.

This level of type safety doesn’t just save time—it builds confidence. And it’s something I believe other frameworks will spend years trying to catch up to.

### Unlock the Power of URL State Management

One of the most overlooked but powerful tools in web development is the URL. It’s the original state management system—fast, shareable, and intuitive. Yet, many frameworks treat the URL as an afterthought, offering only basic utilities for reading and writing state.

TanStack Router flips that script. Managing state in the URL isn’t just supported—it’s encouraged. With intuitive APIs, you can validate, read, and update search parameters with type safety and runtime validation baked in. Want to create a deeply nested, dynamic filter system or synchronize your app’s state with the URL? It’s effortless.

But this isn’t just about developer convenience—it’s about creating better user experiences. When your app state lives in the URL, users can share it, bookmark it, and pick up right where they left off. TanStack Router makes that as easy as it should be.

### Familiar Patterns, More Flexibility

If you’ve worked with Remix or Next.js, you’ll find plenty of familiar concepts in TanStack Start. But familiarity doesn’t mean compromise. We’ve taken some of the best ideas from those frameworks and pushed them further, stripping away the constraints and introducing more flexibility.

For example, routing patterns and server function integrations will feel natural if you’re coming from a server-first framework like Remix, but they’re designed to work just as well for traditional client-side SPAs. You don’t have to pick a side—you get the best of both worlds, with fewer trade-offs.

### Built for the Future (and Already Embracing It)

The web is changing fast. With React Server Components (RSCs) on the horizon, React 19 introducing new patterns, and streaming becoming the standard for data delivery, frameworks need to do more than just keep up—they need to lead.

TanStack Start is ready for what’s next. RSCs are treated as another server-side state, with powerful primitives for caching, invalidating, and composing them into your application. Streaming isn’t an afterthought—it’s baked into the core of how TanStack works, giving you tools to incrementally send data and HTML to the client without extra complexity.

But we’re not just about future-proofing. TanStack Start also makes these advanced capabilities approachable and usable today. You don’t need to wait for the “next big thing” to start building apps that feel like the future.

### SPAs Aren’t Dead (We’re Just Making Them Better)

There’s a lot of talk about server-first architectures these days, and while they’re exciting, they’re not the whole story. Single Page Applications (SPAs) are still an incredible way to build fast, interactive apps—especially when done right.

TanStack Start doesn’t just keep SPAs viable—it makes them better. With simplified patterns, powerful state management, and deep integrations, you can build SPAs that are more performant, easier to maintain, and a joy to use. Whether you’re working server-first, client-first, or somewhere in between, TanStack gives you the tools to build the app you want.

### Data Integration Like No Other

If you’ve used **TanStack Query**, you already know how much it simplifies data-fetching. But the integration between TanStack Query and TanStack Router is where the magic really happens. Prefetching data, caching results, and streaming updates are all seamless, intuitive, and built to scale.

For example, you can prefetch data in a route loader, stream it down to the client, and hydrate it on demand—all with a single API. Whether you’re managing a simple blog or a complex dashboard, you’ll find yourself spending less time wiring up data and more time building features.

This isn’t just an integration—it’s a partnership between routing and data-fetching that makes everything else feel clunky by comparison.

### Routing That Scales

Routing isn’t just a utility—it’s the backbone of every application. And yet, most routers struggle when things get complex. That’s where TanStack Router shines. It’s built to handle everything from a handful of simple routes to thousands of deeply nested ones without breaking a sweat.

Features like type-safe navigation, hierarchical route contexts, and advanced state synchronization make it easy to build apps that scale in both size and complexity. And because TanStack Router works natively with TypeScript, you get all the benefits of type safety without sacrificing performance or flexibility.

### Always Innovating

What excites me most about TanStack is that we’re just getting started. From isomorphic server functions to powerful caching primitives and streamlined React Server Component support, we’re constantly pushing the boundaries of what’s possible. Our goal isn’t just to build great tools—it’s to build tools that help _you_ build better apps.

### Settle for “Good Enough”?

Other frameworks have their strengths, but if you’re looking for tools that are innovative, flexible, and deeply integrated, TanStack Router and Start are in a league of their own. They’re not just solving today’s problems—they’re helping you build apps that are ready for tomorrow.

So why wait? Explore [TanStack Router](https://tanstack.com/router) and [TanStack Start](https://tanstack.com/start) today, and see how much better app development can be.

Let’s build something amazing together!
64 changes: 31 additions & 33 deletions app/components/Doc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,50 +44,48 @@ export function Doc({
const isTocVisible = shouldRenderToc && headings && headings.length > 1

return (
<div className="w-full p-2 md:p-4 xl:p-8">
<div
className={twMerge(
'w-full flex bg-white/70 dark:bg-black/50 mx-auto rounded-xl max-w-[936px]',
isTocVisible && 'max-w-full'
)}
>
<div
className={twMerge(
'flex bg-white/70 dark:bg-black/30 mx-auto rounded-xl max-w-[936px]',
isTocVisible && 'max-w-full'
'flex overflow-auto flex-col w-full p-4 lg:p-6',
isTocVisible && 'border-r border-gray-500/20 !pr-0'
)}
>
{title ? <DocTitle>{title}</DocTitle> : null}
<div className="h-4" />
<div className="h-px bg-gray-500 opacity-20" />
<div className="h-4" />
<div
className={twMerge(
'flex overflow-auto flex-col w-full p-4 lg:p-6',
isTocVisible && 'border-r border-gray-500/20 !pr-0'
'prose prose-gray prose-sm prose-p:leading-7 dark:prose-invert max-w-none',
isTocVisible && 'pr-4 lg:pr-6'
)}
>
{title ? <DocTitle>{title}</DocTitle> : null}
<div className="h-4" />
<div className="h-px bg-gray-500 opacity-20" />
<div className="h-4" />
<div
className={twMerge(
'prose prose-gray prose-sm prose-p:leading-7 dark:prose-invert max-w-none',
isTocVisible && 'pr-4 lg:pr-6'
)}
<Markdown htmlMarkup={markup} />
</div>
<div className="h-12" />
<div className="w-full h-px bg-gray-500 opacity-30" />
<div className="py-4 opacity-70">
<a
href={`https://github.com/${repo}/tree/${branch}/${filePath}`}
className="flex items-center gap-2"
>
<Markdown htmlMarkup={markup} />
</div>
<div className="h-12" />
<div className="w-full h-px bg-gray-500 opacity-30" />
<div className="py-4 opacity-70">
<a
href={`https://github.com/${repo}/tree/${branch}/${filePath}`}
className="flex items-center gap-2"
>
<FaEdit /> Edit on GitHub
</a>
</div>
<div className="h-24" />
<FaEdit /> Edit on GitHub
</a>
</div>

{isTocVisible && (
<div className="max-w-52 w-full hidden 2xl:block transition-all">
<Toc headings={headings} colorFrom={colorFrom} colorTo={colorTo} />
</div>
)}
<div className="h-24" />
</div>

{isTocVisible && (
<div className="max-w-52 w-full hidden 2xl:block transition-all">
<Toc headings={headings} colorFrom={colorFrom} colorTo={colorTo} />
</div>
)}
</div>
)
}
19 changes: 19 additions & 0 deletions app/components/DocContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { HTMLProps } from 'react'
import { twMerge } from 'tailwind-merge'

export function DocContainer({
children,
...props
}: { children: React.ReactNode } & HTMLProps<HTMLDivElement>) {
return (
<div
{...props}
className={twMerge(
'max-w-full space-y-2 md:space-y-6 lg:space-y-8 p-2 md:p-6 lg:p-8',
props.className
)}
>
{children}
</div>
)
}
13 changes: 8 additions & 5 deletions app/components/Toc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,20 @@ export function Toc({ headings, colorFrom, colorTo }: TocProps) {
}, [location])

return (
<nav className="flex flex-col p-2 gap-1 sticky top-2 max-h-screen">
<h3 className="text-[.9em] font-medium px-2">On this page</h3>

<nav className="flex flex-col gap-1 sticky top-2 max-h-screen divide-y divide-gray-500/20">
<div className="p-2">
<h3 className="text-[.9em] font-bold px-2">On this page</h3>
</div>
<ul
className={twMerge('flex flex-col overflow-y-auto gap-0.5 text-[.8em]')}
className={twMerge(
'p-2 flex flex-col overflow-y-auto gap-0.5 text-[.8em]'
)}
>
{headings?.map((heading) => (
<li
key={heading.id}
className={twMerge(
'cursor-pointer py-[.1rem] w-full rounded-lg hover:bg-gray-500 hover:bg-opacity-10',
'cursor-pointer py-[.1rem] w-full rounded hover:bg-gray-500 hover:bg-opacity-10',
headingLevels[heading.level]
)}
>
Expand Down
23 changes: 13 additions & 10 deletions app/routes/$libraryId/$version.docs.$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { seo } from '~/utils/seo'
import { Doc } from '~/components/Doc'
import { loadDocs } from '~/utils/docs'
import { getBranch, getLibrary } from '~/libraries'
import { DocContainer } from '~/components/DocContainer'

export const Route = createFileRoute('/$libraryId/$version/docs/$')({
loader: (ctx) => {
Expand Down Expand Up @@ -38,15 +39,17 @@ function Docs() {
const branch = getBranch(library, version)

return (
<Doc
title={title}
content={content}
repo={library.repo}
branch={branch}
filePath={filePath}
colorFrom={library.colorFrom}
colorTo={library.colorTo}
shouldRenderToc
/>
<DocContainer>
<Doc
title={title}
content={content}
repo={library.repo}
branch={branch}
filePath={filePath}
colorFrom={library.colorFrom}
colorTo={library.colorTo}
shouldRenderToc
/>
</DocContainer>
)
}
23 changes: 13 additions & 10 deletions app/routes/$libraryId/$version.docs.framework.$framework.$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Doc } from '~/components/Doc'
import { loadDocs } from '~/utils/docs'
import { getBranch, getLibrary } from '~/libraries'
import { capitalize } from '~/utils/utils'
import { DocContainer } from '~/components/DocContainer'

export const Route = createFileRoute(
'/$libraryId/$version/docs/framework/$framework/$'
Expand Down Expand Up @@ -44,15 +45,17 @@ function Docs() {
const branch = getBranch(library, version)

return (
<Doc
title={title}
content={content}
repo={library.repo}
branch={branch}
filePath={filePath}
colorFrom={library.colorFrom}
colorTo={library.colorTo}
shouldRenderToc
/>
<DocContainer>
<Doc
title={title}
content={content}
repo={library.repo}
branch={branch}
filePath={filePath}
colorFrom={library.colorFrom}
colorTo={library.colorTo}
shouldRenderToc
/>
</DocContainer>
)
}
30 changes: 22 additions & 8 deletions app/routes/_libraries/blog.$.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createFileRoute, notFound } from '@tanstack/react-router'
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'
Expand All @@ -8,6 +8,8 @@ import { createServerFn } from '@tanstack/start'
import { formatAuthors } from '~/utils/blog'
import { format } from 'date-fns'
import { z } from 'zod'
import { FaArrowLeft } from 'react-icons/fa'
import { DocContainer } from '~/components/DocContainer'

const fetchBlogPost = createServerFn({ method: 'GET' })
.validator(z.string().optional())
Expand Down Expand Up @@ -71,12 +73,24 @@ export default function BlogPost() {
${content}`

return (
<Doc
title={title}
content={blogContent}
repo={'tanstack/tanstack.com'}
branch={'main'}
filePath={filePath}
/>
<DocContainer>
<div>
<Link
from="/blog/$"
to="/blog"
className="font-bold flex items-center gap-2 p-1"
>
<FaArrowLeft />
Back
</Link>
</div>
<Doc
title={title}
content={blogContent}
repo={'tanstack/tanstack.com'}
branch={'main'}
filePath={filePath}
/>
</DocContainer>
)
}
3 changes: 3 additions & 0 deletions app/utils/blog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export function getPostList() {
{
id: 'why-tanstack-start-is-ditching-adapters',
},
{
id: 'why-tanstack-start-and-router',
},
]
}

Expand Down
Binary file modified media/brand.sketch
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b80d36a

Please sign in to comment.