From fd856c110d70c9fd5799cf075f9ed9336d6b6b13 Mon Sep 17 00:00:00 2001 From: jhislop-design Date: Thu, 9 Jul 2026 11:01:16 -0600 Subject: [PATCH] feat(partners): rich AG Grid partner page with subpages Replace the sparse generic AG Grid partner entry with a dedicated, SEO- and AI-search-optimized resource hub at /partners/ag-grid, modeled on the existing Railway partner page and following repo conventions (Tailwind, Card/Button/Collapsible/CodeBlock, seo(), PartnerImage, trackEvent, FAQ + partner JSON-LD). Routing uses a layout + index so subpages branch off the main page: - /partners/ag-grid overview - /partners/ag-grid/getting-started React quickstart (install, render, TanStack Query) - /partners/ag-grid/vs-tanstack-table honest "when to choose which" comparison - /partners/ag-grid/enterprise grouping, pivoting, server-side row model, charts - /partners/ag-grid/studio embedded-analytics dashboards (ag-studio-react) Shared links, tracking, and UI helpers live in components/AgGridPartner so the sponsor UTM tags stay in sync across every page. External links carry ?utm_medium=sponsor&utm_source=tanstack&utm_campaign=partner-page. Co-Authored-By: Claude Opus 4.8 --- src/components/AgGridPartner.tsx | 167 ++++ src/routeTree.gen.ts | 137 ++++ src/routes/partners.ag-grid.enterprise.tsx | 332 ++++++++ .../partners.ag-grid.getting-started.tsx | 338 ++++++++ src/routes/partners.ag-grid.index.tsx | 753 ++++++++++++++++++ src/routes/partners.ag-grid.studio.tsx | 410 ++++++++++ src/routes/partners.ag-grid.tsx | 9 + .../partners.ag-grid.vs-tanstack-table.tsx | 317 ++++++++ 8 files changed, 2463 insertions(+) create mode 100644 src/components/AgGridPartner.tsx create mode 100644 src/routes/partners.ag-grid.enterprise.tsx create mode 100644 src/routes/partners.ag-grid.getting-started.tsx create mode 100644 src/routes/partners.ag-grid.index.tsx create mode 100644 src/routes/partners.ag-grid.studio.tsx create mode 100644 src/routes/partners.ag-grid.tsx create mode 100644 src/routes/partners.ag-grid.vs-tanstack-table.tsx diff --git a/src/components/AgGridPartner.tsx b/src/components/AgGridPartner.tsx new file mode 100644 index 000000000..900d75ac6 --- /dev/null +++ b/src/components/AgGridPartner.tsx @@ -0,0 +1,167 @@ +import * as React from 'react' +import { Link } from '@tanstack/react-router' +import { Check } from 'lucide-react' +import { CodeBlock } from '~/components/markdown/CodeBlock' +import { trackEvent } from '~/utils/analytics' + +/** + * Shared building blocks for the AG Grid partner page and its subpages + * (`/partners/ag-grid/*`). Keeping links, tracking, and small UI helpers in one + * place keeps every page consistent and the sponsor UTM tags in sync. + */ + +const UTM = 'utm_medium=sponsor&utm_source=tanstack&utm_campaign=partner-page' + +export const AG_GRID_LINKS = { + home: `https://www.ag-grid.com/?${UTM}`, + reactGrid: `https://www.ag-grid.com/react-data-grid/?${UTM}`, + docs: `https://www.ag-grid.com/react-data-grid/getting-started/?${UTM}`, + rowGrouping: `https://www.ag-grid.com/react-data-grid/grouping/?${UTM}`, + pivoting: `https://www.ag-grid.com/react-data-grid/pivoting/?${UTM}`, + serverSide: `https://www.ag-grid.com/react-data-grid/server-side-model/?${UTM}`, + integratedCharts: `https://www.ag-grid.com/react-data-grid/integrated-charts/?${UTM}`, + agCharts: `https://www.ag-grid.com/charts/?${UTM}`, + pricing: `https://www.ag-grid.com/license-pricing/?${UTM}`, + studio: `https://www.ag-grid.com/studio/?${UTM}`, + studioTrial: `https://www.ag-grid.com/studio/license-pricing/?tab=trial&${UTM}`, + studioDemos: `https://www.ag-grid.com/studio/example/?${UTM}`, + studioQuickStart: `https://www.ag-grid.com/studio/react/quick-start/?${UTM}`, +} as const + +export function trackAgGridClick(destinationHost = 'ag-grid.com') { + trackEvent('partner_clicked', { + partner_id: 'ag-grid', + placement: 'detail', + destination: 'external', + destination_host: destinationHost, + }) +} + +export function CheckBadge() { + return ( + + + + ) +} + +export function AgGridCodeExample({ + code, + lang, + title, +}: { + code: string + lang: string + title: string +}) { + return ( + + {code} + + ) +} + +type SubpageLinkTo = + | '/partners/ag-grid' + | '/partners/ag-grid/studio' + | '/partners/ag-grid/vs-tanstack-table' + | '/partners/ag-grid/enterprise' + | '/partners/ag-grid/getting-started' + +const SUBPAGES: Array<{ to: SubpageLinkTo; label: string; desc: string }> = [ + { + to: '/partners/ag-grid', + label: 'Overview', + desc: 'AG Grid + TanStack Table, at a glance', + }, + { + to: '/partners/ag-grid/getting-started', + label: 'React quickstart', + desc: 'Add AG Grid to a TanStack app in minutes', + }, + { + to: '/partners/ag-grid/vs-tanstack-table', + label: 'vs TanStack Table', + desc: 'When to choose which grid', + }, + { + to: '/partners/ag-grid/enterprise', + label: 'Enterprise features', + desc: 'Pivoting, grouping, and the server-side row model', + }, + { + to: '/partners/ag-grid/studio', + label: 'AG Grid Studio', + desc: 'Embedded analytics dashboards', + }, +] + +/** Card grid linking to the sibling AG Grid pages. Skips the current page. */ +export function AgGridSubpageNav({ current }: { current: SubpageLinkTo }) { + const items = SUBPAGES.filter((page) => page.to !== current) + + return ( +
+ {items.map((page) => ( + +
+ {page.label} +
+

+ {page.desc} +

+ + ))} +
+ ) +} + +/** Shared breadcrumb: Partners / AG Grid / . */ +export function AgGridBreadcrumb({ current }: { current?: string }) { + return ( + + ) +} + +/** Section heading + intro shared across subpages. */ +export function AgGridSectionIntro({ + title, + children, +}: { + title: string + children: React.ReactNode +}) { + return ( + <> +

+ {title} +

+

+ {children} +

+ + ) +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index fc3a7200d..363fc867e 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -57,6 +57,7 @@ import { Route as ShopSearchRouteImport } from './routes/shop.search' import { Route as ShopCartRouteImport } from './routes/shop.cart' import { Route as ShopHandleRouteImport } from './routes/shop.$handle' import { Route as PartnersRailwayRouteImport } from './routes/partners.railway' +import { Route as PartnersAgGridRouteImport } from './routes/partners.ag-grid' import { Route as PartnersPartnerRouteImport } from './routes/partners.$partner' import { Route as OauthTokenRouteImport } from './routes/oauth/token' import { Route as OauthRegisterRouteImport } from './routes/oauth/register' @@ -82,6 +83,7 @@ import { Route as AccountFeedbackRouteImport } from './routes/account/feedback' import { Route as DotwellKnownOauthAuthorizationServerRouteImport } from './routes/[.]well-known/oauth-authorization-server' import { Route as LibraryLibraryIdRouteRouteImport } from './routes/_library/$libraryId/route' import { Route as StatsNpmIndexRouteImport } from './routes/stats/npm/index' +import { Route as PartnersAgGridIndexRouteImport } from './routes/partners.ag-grid.index' import { Route as IntentRegistryIndexRouteImport } from './routes/intent/registry/index' import { Route as ApiMcpIndexRouteImport } from './routes/api/mcp/index' import { Route as AdminShowcasesIndexRouteImport } from './routes/admin/showcases.index' @@ -96,6 +98,10 @@ import { Route as ShopProductsHandleRouteImport } from './routes/shop.products.$ import { Route as ShopPoliciesHandleRouteImport } from './routes/shop.policies.$handle' import { Route as ShopPagesHandleRouteImport } from './routes/shop.pages.$handle' import { Route as ShopCollectionsHandleRouteImport } from './routes/shop.collections.$handle' +import { Route as PartnersAgGridVsTanstackTableRouteImport } from './routes/partners.ag-grid.vs-tanstack-table' +import { Route as PartnersAgGridStudioRouteImport } from './routes/partners.ag-grid.studio' +import { Route as PartnersAgGridGettingStartedRouteImport } from './routes/partners.ag-grid.getting-started' +import { Route as PartnersAgGridEnterpriseRouteImport } from './routes/partners.ag-grid.enterprise' import { Route as IntentRegistryPackageNameRouteImport } from './routes/intent/registry/$packageName' import { Route as AuthProviderStartRouteImport } from './routes/auth/$provider/start' import { Route as ApiOgChar123Char125DotpngRouteImport } from './routes/api/og/{$}[.]png' @@ -402,6 +408,11 @@ const PartnersRailwayRoute = PartnersRailwayRouteImport.update({ path: '/railway', getParentRoute: () => PartnersRoute, } as any) +const PartnersAgGridRoute = PartnersAgGridRouteImport.update({ + id: '/ag-grid', + path: '/ag-grid', + getParentRoute: () => PartnersRoute, +} as any) const PartnersPartnerRoute = PartnersPartnerRouteImport.update({ id: '/$partner', path: '/$partner', @@ -528,6 +539,11 @@ const StatsNpmIndexRoute = StatsNpmIndexRouteImport.update({ path: '/stats/npm/', getParentRoute: () => rootRouteImport, } as any) +const PartnersAgGridIndexRoute = PartnersAgGridIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PartnersAgGridRoute, +} as any) const IntentRegistryIndexRoute = IntentRegistryIndexRouteImport.update({ id: '/intent/registry/', path: '/intent/registry/', @@ -598,6 +614,29 @@ const ShopCollectionsHandleRoute = ShopCollectionsHandleRouteImport.update({ path: '/collections/$handle', getParentRoute: () => ShopRoute, } as any) +const PartnersAgGridVsTanstackTableRoute = + PartnersAgGridVsTanstackTableRouteImport.update({ + id: '/vs-tanstack-table', + path: '/vs-tanstack-table', + getParentRoute: () => PartnersAgGridRoute, + } as any) +const PartnersAgGridStudioRoute = PartnersAgGridStudioRouteImport.update({ + id: '/studio', + path: '/studio', + getParentRoute: () => PartnersAgGridRoute, +} as any) +const PartnersAgGridGettingStartedRoute = + PartnersAgGridGettingStartedRouteImport.update({ + id: '/getting-started', + path: '/getting-started', + getParentRoute: () => PartnersAgGridRoute, + } as any) +const PartnersAgGridEnterpriseRoute = + PartnersAgGridEnterpriseRouteImport.update({ + id: '/enterprise', + path: '/enterprise', + getParentRoute: () => PartnersAgGridRoute, + } as any) const IntentRegistryPackageNameRoute = IntentRegistryPackageNameRouteImport.update({ id: '/intent/registry/$packageName', @@ -1029,6 +1068,7 @@ export interface FileRoutesByFullPath { '/oauth/register': typeof OauthRegisterRoute '/oauth/token': typeof OauthTokenRoute '/partners/$partner': typeof PartnersPartnerRoute + '/partners/ag-grid': typeof PartnersAgGridRouteWithChildren '/partners/railway': typeof PartnersRailwayRoute '/shop/$handle': typeof ShopHandleRoute '/shop/cart': typeof ShopCartRoute @@ -1069,6 +1109,10 @@ export interface FileRoutesByFullPath { '/api/og/{$}.png': typeof ApiOgChar123Char125DotpngRoute '/auth/$provider/start': typeof AuthProviderStartRoute '/intent/registry/$packageName': typeof IntentRegistryPackageNameRouteWithChildren + '/partners/ag-grid/enterprise': typeof PartnersAgGridEnterpriseRoute + '/partners/ag-grid/getting-started': typeof PartnersAgGridGettingStartedRoute + '/partners/ag-grid/studio': typeof PartnersAgGridStudioRoute + '/partners/ag-grid/vs-tanstack-table': typeof PartnersAgGridVsTanstackTableRoute '/shop/collections/$handle': typeof ShopCollectionsHandleRoute '/shop/pages/$handle': typeof ShopPagesHandleRoute '/shop/policies/$handle': typeof ShopPoliciesHandleRoute @@ -1083,6 +1127,7 @@ export interface FileRoutesByFullPath { '/admin/showcases/': typeof AdminShowcasesIndexRoute '/api/mcp/': typeof ApiMcpIndexRoute '/intent/registry/': typeof IntentRegistryIndexRoute + '/partners/ag-grid/': typeof PartnersAgGridIndexRoute '/stats/npm/': typeof StatsNpmIndexRoute '/$libraryId/$version/docs': typeof LibraryLibraryIdVersionDocsRouteWithChildren '/$libraryId/$version/llms.txt': typeof LibraryLibraryIdVersionLlmsDottxtRoute @@ -1214,6 +1259,10 @@ export interface FileRoutesByTo { '/api/mcp/$': typeof ApiMcpSplatRoute '/api/og/{$}.png': typeof ApiOgChar123Char125DotpngRoute '/auth/$provider/start': typeof AuthProviderStartRoute + '/partners/ag-grid/enterprise': typeof PartnersAgGridEnterpriseRoute + '/partners/ag-grid/getting-started': typeof PartnersAgGridGettingStartedRoute + '/partners/ag-grid/studio': typeof PartnersAgGridStudioRoute + '/partners/ag-grid/vs-tanstack-table': typeof PartnersAgGridVsTanstackTableRoute '/shop/collections/$handle': typeof ShopCollectionsHandleRoute '/shop/pages/$handle': typeof ShopPagesHandleRoute '/shop/policies/$handle': typeof ShopPoliciesHandleRoute @@ -1228,6 +1277,7 @@ export interface FileRoutesByTo { '/admin/showcases': typeof AdminShowcasesIndexRoute '/api/mcp': typeof ApiMcpIndexRoute '/intent/registry': typeof IntentRegistryIndexRoute + '/partners/ag-grid': typeof PartnersAgGridIndexRoute '/stats/npm': typeof StatsNpmIndexRoute '/$libraryId/$version/llms.txt': typeof LibraryLibraryIdVersionLlmsDottxtRoute '/api/auth/callback/$provider': typeof ApiAuthCallbackProviderRoute @@ -1329,6 +1379,7 @@ export interface FileRoutesById { '/oauth/register': typeof OauthRegisterRoute '/oauth/token': typeof OauthTokenRoute '/partners/$partner': typeof PartnersPartnerRoute + '/partners/ag-grid': typeof PartnersAgGridRouteWithChildren '/partners/railway': typeof PartnersRailwayRoute '/shop/$handle': typeof ShopHandleRoute '/shop/cart': typeof ShopCartRoute @@ -1369,6 +1420,10 @@ export interface FileRoutesById { '/api/og/{$}.png': typeof ApiOgChar123Char125DotpngRoute '/auth/$provider/start': typeof AuthProviderStartRoute '/intent/registry/$packageName': typeof IntentRegistryPackageNameRouteWithChildren + '/partners/ag-grid/enterprise': typeof PartnersAgGridEnterpriseRoute + '/partners/ag-grid/getting-started': typeof PartnersAgGridGettingStartedRoute + '/partners/ag-grid/studio': typeof PartnersAgGridStudioRoute + '/partners/ag-grid/vs-tanstack-table': typeof PartnersAgGridVsTanstackTableRoute '/shop/collections/$handle': typeof ShopCollectionsHandleRoute '/shop/pages/$handle': typeof ShopPagesHandleRoute '/shop/policies/$handle': typeof ShopPoliciesHandleRoute @@ -1383,6 +1438,7 @@ export interface FileRoutesById { '/admin/showcases/': typeof AdminShowcasesIndexRoute '/api/mcp/': typeof ApiMcpIndexRoute '/intent/registry/': typeof IntentRegistryIndexRoute + '/partners/ag-grid/': typeof PartnersAgGridIndexRoute '/stats/npm/': typeof StatsNpmIndexRoute '/_library/$libraryId/$version/docs': typeof LibraryLibraryIdVersionDocsRouteWithChildren '/_library/$libraryId/$version/llms.txt': typeof LibraryLibraryIdVersionLlmsDottxtRoute @@ -1485,6 +1541,7 @@ export interface FileRouteTypes { | '/oauth/register' | '/oauth/token' | '/partners/$partner' + | '/partners/ag-grid' | '/partners/railway' | '/shop/$handle' | '/shop/cart' @@ -1525,6 +1582,10 @@ export interface FileRouteTypes { | '/api/og/{$}.png' | '/auth/$provider/start' | '/intent/registry/$packageName' + | '/partners/ag-grid/enterprise' + | '/partners/ag-grid/getting-started' + | '/partners/ag-grid/studio' + | '/partners/ag-grid/vs-tanstack-table' | '/shop/collections/$handle' | '/shop/pages/$handle' | '/shop/policies/$handle' @@ -1539,6 +1600,7 @@ export interface FileRouteTypes { | '/admin/showcases/' | '/api/mcp/' | '/intent/registry/' + | '/partners/ag-grid/' | '/stats/npm/' | '/$libraryId/$version/docs' | '/$libraryId/$version/llms.txt' @@ -1670,6 +1732,10 @@ export interface FileRouteTypes { | '/api/mcp/$' | '/api/og/{$}.png' | '/auth/$provider/start' + | '/partners/ag-grid/enterprise' + | '/partners/ag-grid/getting-started' + | '/partners/ag-grid/studio' + | '/partners/ag-grid/vs-tanstack-table' | '/shop/collections/$handle' | '/shop/pages/$handle' | '/shop/policies/$handle' @@ -1684,6 +1750,7 @@ export interface FileRouteTypes { | '/admin/showcases' | '/api/mcp' | '/intent/registry' + | '/partners/ag-grid' | '/stats/npm' | '/$libraryId/$version/llms.txt' | '/api/auth/callback/$provider' @@ -1784,6 +1851,7 @@ export interface FileRouteTypes { | '/oauth/register' | '/oauth/token' | '/partners/$partner' + | '/partners/ag-grid' | '/partners/railway' | '/shop/$handle' | '/shop/cart' @@ -1824,6 +1892,10 @@ export interface FileRouteTypes { | '/api/og/{$}.png' | '/auth/$provider/start' | '/intent/registry/$packageName' + | '/partners/ag-grid/enterprise' + | '/partners/ag-grid/getting-started' + | '/partners/ag-grid/studio' + | '/partners/ag-grid/vs-tanstack-table' | '/shop/collections/$handle' | '/shop/pages/$handle' | '/shop/policies/$handle' @@ -1838,6 +1910,7 @@ export interface FileRouteTypes { | '/admin/showcases/' | '/api/mcp/' | '/intent/registry/' + | '/partners/ag-grid/' | '/stats/npm/' | '/_library/$libraryId/$version/docs' | '/_library/$libraryId/$version/llms.txt' @@ -2301,6 +2374,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof PartnersRailwayRouteImport parentRoute: typeof PartnersRoute } + '/partners/ag-grid': { + id: '/partners/ag-grid' + path: '/ag-grid' + fullPath: '/partners/ag-grid' + preLoaderRoute: typeof PartnersAgGridRouteImport + parentRoute: typeof PartnersRoute + } '/partners/$partner': { id: '/partners/$partner' path: '/$partner' @@ -2476,6 +2556,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof StatsNpmIndexRouteImport parentRoute: typeof rootRouteImport } + '/partners/ag-grid/': { + id: '/partners/ag-grid/' + path: '/' + fullPath: '/partners/ag-grid/' + preLoaderRoute: typeof PartnersAgGridIndexRouteImport + parentRoute: typeof PartnersAgGridRoute + } '/intent/registry/': { id: '/intent/registry/' path: '/intent/registry' @@ -2574,6 +2661,34 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ShopCollectionsHandleRouteImport parentRoute: typeof ShopRoute } + '/partners/ag-grid/vs-tanstack-table': { + id: '/partners/ag-grid/vs-tanstack-table' + path: '/vs-tanstack-table' + fullPath: '/partners/ag-grid/vs-tanstack-table' + preLoaderRoute: typeof PartnersAgGridVsTanstackTableRouteImport + parentRoute: typeof PartnersAgGridRoute + } + '/partners/ag-grid/studio': { + id: '/partners/ag-grid/studio' + path: '/studio' + fullPath: '/partners/ag-grid/studio' + preLoaderRoute: typeof PartnersAgGridStudioRouteImport + parentRoute: typeof PartnersAgGridRoute + } + '/partners/ag-grid/getting-started': { + id: '/partners/ag-grid/getting-started' + path: '/getting-started' + fullPath: '/partners/ag-grid/getting-started' + preLoaderRoute: typeof PartnersAgGridGettingStartedRouteImport + parentRoute: typeof PartnersAgGridRoute + } + '/partners/ag-grid/enterprise': { + id: '/partners/ag-grid/enterprise' + path: '/enterprise' + fullPath: '/partners/ag-grid/enterprise' + preLoaderRoute: typeof PartnersAgGridEnterpriseRouteImport + parentRoute: typeof PartnersAgGridRoute + } '/intent/registry/$packageName': { id: '/intent/registry/$packageName' path: '/intent/registry/$packageName' @@ -3265,14 +3380,36 @@ const BuilderRouteChildren: BuilderRouteChildren = { const BuilderRouteWithChildren = BuilderRoute._addFileChildren(BuilderRouteChildren) +interface PartnersAgGridRouteChildren { + PartnersAgGridEnterpriseRoute: typeof PartnersAgGridEnterpriseRoute + PartnersAgGridGettingStartedRoute: typeof PartnersAgGridGettingStartedRoute + PartnersAgGridStudioRoute: typeof PartnersAgGridStudioRoute + PartnersAgGridVsTanstackTableRoute: typeof PartnersAgGridVsTanstackTableRoute + PartnersAgGridIndexRoute: typeof PartnersAgGridIndexRoute +} + +const PartnersAgGridRouteChildren: PartnersAgGridRouteChildren = { + PartnersAgGridEnterpriseRoute: PartnersAgGridEnterpriseRoute, + PartnersAgGridGettingStartedRoute: PartnersAgGridGettingStartedRoute, + PartnersAgGridStudioRoute: PartnersAgGridStudioRoute, + PartnersAgGridVsTanstackTableRoute: PartnersAgGridVsTanstackTableRoute, + PartnersAgGridIndexRoute: PartnersAgGridIndexRoute, +} + +const PartnersAgGridRouteWithChildren = PartnersAgGridRoute._addFileChildren( + PartnersAgGridRouteChildren, +) + interface PartnersRouteChildren { PartnersPartnerRoute: typeof PartnersPartnerRoute + PartnersAgGridRoute: typeof PartnersAgGridRouteWithChildren PartnersRailwayRoute: typeof PartnersRailwayRoute PartnersIndexRoute: typeof PartnersIndexRoute } const PartnersRouteChildren: PartnersRouteChildren = { PartnersPartnerRoute: PartnersPartnerRoute, + PartnersAgGridRoute: PartnersAgGridRouteWithChildren, PartnersRailwayRoute: PartnersRailwayRoute, PartnersIndexRoute: PartnersIndexRoute, } diff --git a/src/routes/partners.ag-grid.enterprise.tsx b/src/routes/partners.ag-grid.enterprise.tsx new file mode 100644 index 000000000..4f0a7a92e --- /dev/null +++ b/src/routes/partners.ag-grid.enterprise.tsx @@ -0,0 +1,332 @@ +import * as React from 'react' +import { createFileRoute } from '@tanstack/react-router' +import { + ArrowUpRight, + BarChart3, + Clipboard, + FileSpreadsheet, + FolderTree, + Grid3x3, + Layers, + Plus, + Rows3, + Server, + SlidersHorizontal, +} from 'lucide-react' +import { twMerge } from 'tailwind-merge' +import { Footer } from '~/components/Footer' +import { Card } from '~/components/Card' +import { Button } from '~/ui' +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '~/components/Collapsible' +import { seo } from '~/utils/seo' +import { trackEvent } from '~/utils/analytics' +import { + AG_GRID_LINKS, + AgGridBreadcrumb, + AgGridCodeExample, + AgGridSubpageNav, + trackAgGridClick, +} from '~/components/AgGridPartner' + +const ENTERPRISE_SNIPPET = `import { AgGridReact } from 'ag-grid-react' +import { ModuleRegistry } from 'ag-grid-community' +import { + AllEnterpriseModule, + LicenseManager, +} from 'ag-grid-enterprise' + +// Register once, at app startup. +ModuleRegistry.registerModules([AllEnterpriseModule]) +LicenseManager.setLicenseKey(process.env.AG_GRID_LICENSE_KEY!) + +export function AnalyticsGrid({ rows }: { rows: Array }) { + return ( +
+ +
+ ) +} +` + +type FeatureIcon = React.ComponentType<{ className?: string }> + +const features: Array<{ Icon: FeatureIcon; title: string; desc: string }> = [ + { + Icon: Layers, + title: 'Row grouping & aggregation', + desc: 'Group by any column and roll up sums, averages, and custom aggregations, with a drag-to-group panel for users.', + }, + { + Icon: Grid3x3, + title: 'Pivoting', + desc: 'Pivot rows into columns interactively — the spreadsheet move your analysts expect, without reshaping data server-side.', + }, + { + Icon: Server, + title: 'Server-side row model', + desc: 'Sort, filter, group, and paginate millions of rows straight from your backend. The grid only requests what it renders.', + }, + { + Icon: BarChart3, + title: 'Integrated charts', + desc: 'Let users select a range and chart it in place with AG Charts — no separate charting stack to wire up.', + }, + { + Icon: FileSpreadsheet, + title: 'Excel export', + desc: 'Export styled, multi-sheet Excel files including groups, formatting, and formulas, straight from the grid.', + }, + { + Icon: Rows3, + title: 'Master / detail', + desc: 'Expand any row into a nested detail grid for drill-down views without a separate page or modal.', + }, + { + Icon: FolderTree, + title: 'Tree data', + desc: 'Render hierarchical data — file systems, org charts, nested categories — with lazy loading built in.', + }, + { + Icon: Clipboard, + title: 'Range selection & clipboard', + desc: 'Excel-style range selection, fill handle, and clipboard interop so power users feel at home.', + }, + { + Icon: SlidersHorizontal, + title: 'Tool panels & status bar', + desc: 'Built-in columns and filters tool panels plus an aggregation status bar for rich, self-service data exploration.', + }, +] + +const faqs: Array<{ q: string; a: string }> = [ + { + q: 'What do I get with AG Grid Enterprise?', + a: 'Enterprise adds row grouping and aggregation, pivoting, the server-side row model, integrated charts, Excel export, master/detail, tree data, range selection with a fill handle, tool panels, and a status bar — on top of everything in the free Community edition.', + }, + { + q: 'How is Enterprise licensed?', + a: 'AG Grid Enterprise requires a commercial license. You install ag-grid-enterprise and call LicenseManager.setLicenseKey() once at startup. Without a key it still runs for evaluation, showing a watermark and console notice. Licenses are per-developer and cover production use.', + }, + { + q: 'How many rows can the server-side row model handle?', + a: 'The server-side row model is designed for datasets that are too large to load at once. Because the grid only requests the rows, groups, and pages it needs, it comfortably drives grids over millions of rows backed by your API or database.', + }, + { + q: 'Can I use TanStack Query with the server-side row model?', + a: 'Yes. A common pattern is to have your getRows datasource call a TanStack Query-backed endpoint. Query owns fetching and caching; AG Grid owns the sorting, filtering, grouping, and pagination requests it sends.', + }, +] + +const PAGE_TITLE = 'AG Grid Enterprise — Features for Large-Scale Data Grids' +const PAGE_DESCRIPTION = + 'AG Grid Enterprise adds row grouping, pivoting, the server-side row model, integrated charts, Excel export, master/detail, and tree data on top of the free Community edition. Drive grids over millions of rows in your TanStack app.' + +function getFaqJsonLd() { + return { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: faqs.map((faq) => ({ + '@type': 'Question', + name: faq.q, + acceptedAnswer: { '@type': 'Answer', text: faq.a }, + })), + } +} + +export const Route = createFileRoute('/partners/ag-grid/enterprise')({ + head: () => ({ + meta: seo({ + title: PAGE_TITLE, + description: PAGE_DESCRIPTION, + keywords: + 'ag grid enterprise, server-side row model, ag grid pivoting, ag grid row grouping, integrated charts, ag grid excel export', + image: 'https://tanstack.com/og.png', + }), + scripts: [ + { + type: 'application/ld+json', + children: JSON.stringify(getFaqJsonLd()), + }, + ], + }), + component: AgGridEnterprisePage, +}) + +function AgGridEnterprisePage() { + const [openFaq, setOpenFaq] = React.useState(null) + + React.useEffect(() => { + trackEvent('partner_viewed', { + partner_id: 'ag-grid', + placement: 'detail', + }) + }, []) + + return ( +
+
+ + + {/* Hero */} +
+ + AG Grid Enterprise + +

+ When a table isn't +
+ enough anymore +

+

+ Enterprise unlocks the heavy-duty features teams reach for at scale + — pivoting, row grouping, a server-side row model for millions of + rows, integrated charts, and Excel export — all on top of the free + Community edition. +

+ +
+ + +
+
+ + {/* Features */} +
+

+ What Enterprise adds +

+

+ Everything below is opt-in and tree-shakeable through module + registration. +

+
+ {features.map(({ Icon, title, desc }) => ( + + +
{title}
+

+ {desc} +

+
+ ))} +
+

+ Integrated charts are powered by{' '} + trackAgGridClick()} + className="underline decoration-dotted underline-offset-2 hover:text-blue-500" + > + AG Charts + {' '} + — 30+ chart types, with free Community and Enterprise editions. +

+
+ + {/* Setup */} +
+

+ Turn on Enterprise +

+

+ Install the Enterprise package, register the modules, and set your + license key once at startup. Then features like pivoting and + grouping are just column definitions. +

+
+ +
+
+ + {/* FAQ */} +
+

+ Enterprise FAQ +

+
+ {faqs.map(({ q, a }, i) => { + const isOpen = openFaq === i + return ( + setOpenFaq(next ? i : null)} + className="border-b border-gray-200 dark:border-gray-800" + > + + + {q} + + + + +

+ {a} +

+
+
+ ) + })} +
+
+ + {/* Explore more */} +
+

+ Explore more +

+
+ +
+
+
+ +
+
+ ) +} diff --git a/src/routes/partners.ag-grid.getting-started.tsx b/src/routes/partners.ag-grid.getting-started.tsx new file mode 100644 index 000000000..02f4d397f --- /dev/null +++ b/src/routes/partners.ag-grid.getting-started.tsx @@ -0,0 +1,338 @@ +import * as React from 'react' +import { createFileRoute } from '@tanstack/react-router' +import { ArrowUpRight, Plus } from 'lucide-react' +import { twMerge } from 'tailwind-merge' +import { Footer } from '~/components/Footer' +import { Card } from '~/components/Card' +import { Button } from '~/ui' +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '~/components/Collapsible' +import { seo } from '~/utils/seo' +import { trackEvent } from '~/utils/analytics' +import { + AG_GRID_LINKS, + AgGridBreadcrumb, + AgGridCodeExample, + AgGridSubpageNav, + trackAgGridClick, +} from '~/components/AgGridPartner' + +const INSTALL_SNIPPET = `# Free, MIT-licensed Community edition +npm install ag-grid-react ag-grid-community +` + +const GRID_SNIPPET = `import { useState } from 'react' +import { AgGridReact } from 'ag-grid-react' +import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community' + +// Register the Community modules once, at startup. +ModuleRegistry.registerModules([AllCommunityModule]) + +export function CarGrid() { + const [rowData] = useState([ + { make: 'Tesla', model: 'Model Y', price: 64950 }, + { make: 'Ford', model: 'F-Series', price: 33850 }, + { make: 'Toyota', model: 'Corolla', price: 29600 }, + ]) + const [columnDefs] = useState([ + { field: 'make' }, + { field: 'model' }, + { field: 'price' }, + ]) + + // A container height is required — the grid virtualizes its rows. + return ( +
+ +
+ ) +} +` + +const QUERY_SNIPPET = `import { useQuery } from '@tanstack/react-query' +import { AgGridReact } from 'ag-grid-react' + +export function CarsRoute() { + const { data = [], isLoading } = useQuery({ + queryKey: ['cars'], + queryFn: () => fetch('/api/cars').then((r) => r.json()), + }) + + return ( +
+ +
+ ) +} +` + +const steps: Array<{ num: string; title: string; desc: string }> = [ + { + num: '01', + title: 'Create a TanStack app', + desc: 'Start from a TanStack Start or Router app — AG Grid renders on the client after hydration, so any route works.', + }, + { + num: '02', + title: 'Install AG Grid', + desc: 'Add the free Community packages: ag-grid-react and ag-grid-community.', + }, + { + num: '03', + title: 'Register modules and render', + desc: 'Register the Community modules once, then render AgGridReact with columnDefs, rowData, and a container height.', + }, + { + num: '04', + title: 'Wire up data with TanStack Query', + desc: 'Feed rowData from a Query result. Query owns fetching and caching; the grid owns sorting, filtering, and display.', + }, +] + +const faqs: Array<{ q: string; a: string }> = [ + { + q: 'Does AG Grid need a container height?', + a: 'Yes. AG Grid virtualizes rows, so it needs a bounded height to know how many to render. Give the wrapping element an explicit height (for example height: 500px) or a flex layout that constrains it. DomLayout autoHeight is available for small, non-virtualized grids.', + }, + { + q: 'Does AG Grid render on the server with TanStack Start?', + a: 'AG Grid is a client-side component and mounts after hydration. Your TanStack Start route can server-render the surrounding page and data; the grid itself initializes in the browser. Load row data with a loader or TanStack Query and pass it in as rowData.', + }, + { + q: 'How do I add sorting and filtering?', + a: 'Set a defaultColDef with sortable, filter, and resizable to enable them across all columns, or configure them per column. These are part of the free Community edition.', + }, + { + q: 'How do I upgrade to Enterprise features?', + a: 'Install ag-grid-enterprise, register the Enterprise modules, and call LicenseManager.setLicenseKey() once. Features like pivoting and the server-side row model then become column and grid options. See the Enterprise page for details.', + }, +] + +const PAGE_TITLE = 'AG Grid + TanStack — React Quickstart' +const PAGE_DESCRIPTION = + 'Add AG Grid to a TanStack Start or Router app in minutes. Install the free Community packages, register modules, render a grid, and wire up data with TanStack Query. Copy-paste React code included.' + +function getFaqJsonLd() { + return { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: faqs.map((faq) => ({ + '@type': 'Question', + name: faq.q, + acceptedAnswer: { '@type': 'Answer', text: faq.a }, + })), + } +} + +export const Route = createFileRoute('/partners/ag-grid/getting-started')({ + head: () => ({ + meta: seo({ + title: PAGE_TITLE, + description: PAGE_DESCRIPTION, + keywords: + 'ag grid react tanstack, ag grid getting started, ag grid tanstack query, ag grid tanstack start, react data grid tutorial', + image: 'https://tanstack.com/og.png', + }), + scripts: [ + { + type: 'application/ld+json', + children: JSON.stringify(getFaqJsonLd()), + }, + ], + }), + component: AgGridQuickstartPage, +}) + +function AgGridQuickstartPage() { + const [openFaq, setOpenFaq] = React.useState(null) + + React.useEffect(() => { + trackEvent('partner_viewed', { + partner_id: 'ag-grid', + placement: 'detail', + }) + }, []) + + return ( +
+
+ + + {/* Hero */} +
+ + React quickstart + +

+ Add AG Grid to your +
+ TanStack app +

+

+ AG Grid is a client-side React component, so it drops into any + TanStack Start or Router route. Install the free Community packages, + render a grid, and feed it with TanStack Query. +

+ +
+ +
+

+ Community is free forever and MIT-licensed. No credit card required. +

+
+ + {/* Steps */} +
+

+ Four steps to a working grid +

+
+ {steps.map(({ num, title, desc }) => ( + +
+ {num} +
+
+
{title}
+

+ {desc} +

+
+
+ ))} +
+
+ + {/* Install + render */} +
+

+ Install and render +

+

+ Two packages, one module registration, and a grid with columns and + rows. +

+
+ + +
+
+ + {/* TanStack Query */} +
+

+ Wire up data with TanStack Query +

+

+ Let Query handle fetching and caching, then pass the result straight + into the grid as rowData. +

+
+ +
+ +
+ + +
+
+ + {/* FAQ */} +
+

+ Quickstart FAQ +

+
+ {faqs.map(({ q, a }, i) => { + const isOpen = openFaq === i + return ( + setOpenFaq(next ? i : null)} + className="border-b border-gray-200 dark:border-gray-800" + > + + + {q} + + + + +

+ {a} +

+
+
+ ) + })} +
+
+ + {/* Explore more */} +
+

+ Explore more +

+
+ +
+
+
+ +
+
+ ) +} diff --git a/src/routes/partners.ag-grid.index.tsx b/src/routes/partners.ag-grid.index.tsx new file mode 100644 index 000000000..a93ed9e78 --- /dev/null +++ b/src/routes/partners.ag-grid.index.tsx @@ -0,0 +1,753 @@ +import * as React from 'react' +import { Link, createFileRoute } from '@tanstack/react-router' +import { + ArrowUpRight, + BarChart3, + Check, + FileSpreadsheet, + FolderTree, + Grid3x3, + Layers, + LayoutDashboard, + Palette, + Plus, + Rows3, + Server, +} from 'lucide-react' +import { twMerge } from 'tailwind-merge' +import { Footer } from '~/components/Footer' +import { Card } from '~/components/Card' +import { Button } from '~/ui' +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '~/components/Collapsible' +import { CodeBlock } from '~/components/markdown/CodeBlock' +import { seo } from '~/utils/seo' +import { getPartnerById, PartnerImage } from '~/utils/partners' +import { getPartnerJsonLd } from '~/utils/partner-pages' +import { trackEvent } from '~/utils/analytics' +import { AgGridSubpageNav } from '~/components/AgGridPartner' + +const UTM = 'utm_medium=sponsor&utm_source=tanstack&utm_campaign=partner-page' +const AG_GRID_HREF = `https://www.ag-grid.com/react-data-grid/?${UTM}` +const AG_GRID_DOCS_HREF = `https://www.ag-grid.com/react-data-grid/getting-started/?${UTM}` +const AG_GRID_PRICING_HREF = `https://www.ag-grid.com/license-pricing/?${UTM}` +const AG_STUDIO_HREF = `https://www.ag-grid.com/studio/?${UTM}` +const AG_STUDIO_TRIAL_HREF = `https://www.ag-grid.com/studio/license-pricing/?tab=trial&${UTM}` + +const INSTALL_SNIPPET = `# Community edition — free and MIT-licensed +npm install ag-grid-react ag-grid-community +` + +const GRID_SNIPPET = `import { useState } from 'react' +import { AgGridReact } from 'ag-grid-react' +import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community' + +ModuleRegistry.registerModules([AllCommunityModule]) + +export function CarGrid() { + const [rowData] = useState([ + { make: 'Tesla', model: 'Model Y', price: 64950 }, + { make: 'Ford', model: 'F-Series', price: 33850 }, + { make: 'Toyota', model: 'Corolla', price: 29600 }, + ]) + const [columnDefs] = useState([ + { field: 'make' }, + { field: 'model' }, + { field: 'price' }, + ]) + + // A container height is required — the grid virtualizes rows. + return ( +
+ +
+ ) +} +` + +type FeatureIcon = React.ComponentType<{ className?: string }> + +const features: Array<{ Icon: FeatureIcon; title: string; desc: string }> = [ + { + Icon: Layers, + title: 'Row grouping & aggregation', + desc: 'Group rows by any column and roll up totals, averages, and custom aggregations across millions of records.', + }, + { + Icon: Grid3x3, + title: 'Pivoting', + desc: 'Pivot rows into columns on the fly, just like a spreadsheet, without reshaping your data on the server.', + }, + { + Icon: Server, + title: 'Server-side row model', + desc: 'Stream, sort, filter, and group huge datasets straight from your backend — the grid only fetches what it renders.', + }, + { + Icon: FileSpreadsheet, + title: 'Excel export', + desc: 'Export styled, multi-sheet Excel files directly from the grid, including grouping and formatting.', + }, + { + Icon: BarChart3, + title: 'Integrated charts', + desc: 'Turn any selection into an interactive chart powered by AG Charts, without leaving the grid.', + }, + { + Icon: Rows3, + title: 'Master / detail', + desc: 'Expand any row into a nested detail grid for drill-down views without a separate page or modal.', + }, + { + Icon: FolderTree, + title: 'Tree data', + desc: 'Render hierarchical data — file systems, org charts, nested categories — with lazy loading built in.', + }, + { + Icon: Palette, + title: 'Theming engine', + desc: 'A modern theming API with design tokens, shared with AG Grid Studio so grids and dashboards match.', + }, +] + +const steps: Array<{ num: string; title: string; code: string }> = [ + { + num: '01', + title: 'Install the Community packages', + code: 'npm install ag-grid-react ag-grid-community', + }, + { + num: '02', + title: 'Register the Community modules', + code: 'ModuleRegistry.registerModules([AllCommunityModule])', + }, + { + num: '03', + title: 'Render a grid with columns and row data', + code: '', + }, + { + num: '04', + title: 'Unlock Enterprise when you need it', + code: 'npm install ag-grid-enterprise', + }, +] + +// Instead of Railway-style customer testimonials, AG Grid's most useful +// section is the honest "when to choose which" guidance — the founding goal +// of the TanStack Table + AG Grid partnership. +const chooseTanStackTable: Array = [ + 'You want a headless grid you style and compose yourself', + 'You need a tiny bundle and full control over the DOM', + 'You are building a bespoke table UI with your own design system', + 'You want the same core across React, Vue, Solid, Svelte, Qwik, and Angular', +] + +const chooseAgGrid: Array = [ + 'You need enterprise features like pivoting or a server-side row model out of the box', + 'You are handling hundreds of thousands to millions of rows', + 'You want Excel export, integrated charts, and range selection without building them', + 'You need a batteries-included grid your team can ship this week', +] + +const libDetails: Array<{ label: string; desc: string }> = [ + { + label: 'TanStack Table', + desc: 'AG Grid is the official open-source partner of TanStack Table. Reach for Table when you want a headless grid, and AG Grid when you need a batteries-included one.', + }, + { + label: 'TanStack Query', + desc: "Feed AG Grid's server-side row model from a Query-backed API. Query owns fetching and caching; the grid owns sorting, filtering, and grouping at scale.", + }, + { + label: 'TanStack Start', + desc: 'AG Grid renders on the client after hydration inside a TanStack Start app — drop it into any route with a container height and column definitions.', + }, + { + label: 'TanStack Virtual', + desc: 'AG Grid ships its own row and column virtualization. Use TanStack Virtual for custom virtualized surfaces outside of a grid.', + }, +] + +const studioFeatures: Array<{ title: string; desc: string }> = [ + { + title: 'Drag & drop dashboards', + desc: 'Rearrange and resize charts, grids, and KPI tiles in real time — no rebuild required.', + }, + { + title: 'Widget gallery', + desc: 'Pre-built bar, line, area, pie, and scatter charts, plus grids and KPI tiles ready to drop in.', + }, + { + title: 'AI assistant', + desc: 'Ask questions of your data in natural language and generate dashboards on the fly.', + }, + { + title: 'Edit and view modes', + desc: 'Configure in edit mode, then hand a clean, read-only reporting view to stakeholders.', + }, +] + +const faqs: Array<{ q: string; a: string }> = [ + { + q: 'Is AG Grid free?', + a: 'AG Grid Community is free and MIT-licensed for commercial use, and covers sorting, filtering, editing, virtualization, and custom cell rendering. AG Grid Enterprise and AG Grid Studio require a commercial license and add features like pivoting, the server-side row model, integrated charts, and embedded dashboards.', + }, + { + q: 'What is the difference between AG Grid and TanStack Table?', + a: 'TanStack Table is a headless library: it gives you the state and logic for a table and you bring the markup and styles. AG Grid is a full, batteries-included data grid with its own rendering and enterprise features. They are complementary — that is why AG Grid is the official open-source partner of TanStack Table.', + }, + { + q: 'What is AG Grid Studio?', + a: 'AG Grid Studio is an embedded analytics toolkit for building drag-and-drop dashboards on top of AG Grid and AG Charts. It handles data processing over hundreds of thousands of rows, ships a widget gallery, and includes an AI assistant. It offers a 45-day free trial with full features and no watermarks.', + }, + { + q: 'Does AG Grid work with TanStack Start?', + a: 'Yes. AG Grid is a client-side React component, so it renders inside any TanStack Start route after hydration. Install ag-grid-react and ag-grid-community, register the Community modules, and give the grid a container with an explicit height.', + }, + { + q: 'Can I use AG Grid with TanStack Query?', + a: "Yes. A common pattern is to let TanStack Query own data fetching and caching while AG Grid's server-side row model requests only the rows it needs to display. Query handles the network; the grid handles sorting, filtering, and grouping at scale.", + }, + { + q: 'How many rows can AG Grid handle?', + a: 'AG Grid virtualizes both rows and columns, so it renders only what is on screen. With the client-side row model it comfortably handles tens of thousands of rows, and with the Enterprise server-side row model it streams millions of rows straight from your backend.', + }, +] + +const PAGE_TITLE = 'AG Grid for TanStack — Official Table Partner' +const PAGE_DESCRIPTION = + 'AG Grid is the official open-source partner of TanStack Table: a batteries-included enterprise data grid with row grouping, pivoting, a server-side row model, Excel export, and integrated charts. Plus AG Grid Studio for embedded analytics dashboards. Learn when to choose AG Grid vs TanStack Table.' + +function getFaqJsonLd() { + return { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: faqs.map((faq) => ({ + '@type': 'Question', + name: faq.q, + acceptedAnswer: { + '@type': 'Answer', + text: faq.a, + }, + })), + } +} + +export const Route = createFileRoute('/partners/ag-grid/')({ + head: () => { + const partner = getPartnerById('ag-grid') + + return { + meta: seo({ + title: PAGE_TITLE, + description: PAGE_DESCRIPTION, + keywords: + 'ag grid tanstack, ag grid vs tanstack table, ag grid react, enterprise data grid, ag grid studio, ag grid partner, tanstack table alternative', + image: 'https://tanstack.com/og.png', + }), + scripts: [ + ...(partner + ? [ + { + type: 'application/ld+json', + children: JSON.stringify(getPartnerJsonLd(partner)), + }, + ] + : []), + { + type: 'application/ld+json', + children: JSON.stringify(getFaqJsonLd()), + }, + ], + } + }, + component: AgGridPartnerPage, +}) + +function CheckBadge() { + return ( + + + + ) +} + +function trackAgGridClick(destinationHost = 'ag-grid.com') { + trackEvent('partner_clicked', { + partner_id: 'ag-grid', + placement: 'detail', + destination: 'external', + destination_host: destinationHost, + }) +} + +function AgGridCodeExample({ + code, + lang, + title, +}: { + code: string + lang: string + title: string +}) { + return ( + + {code} + + ) +} + +function AgGridPartnerPage() { + const [openFaq, setOpenFaq] = React.useState(null) + + React.useEffect(() => { + trackEvent('partner_viewed', { + partner_id: 'ag-grid', + placement: 'detail', + }) + }, []) + + const partner = getPartnerById('ag-grid') + + return ( +
+
+ + + {/* Hero */} +
+
+ {partner ? ( +
+ +
+ ) : null} +
+ + Official Table Partner · Data Grid + +
+ + + Community edition is free & MIT-licensed + +
+
+
+ +

+ The enterprise data grid, +
+ alongside TanStack Table +

+ +

+ AG Grid is the official open-source partner of TanStack Table. When + a headless table needs to become a full data grid — pivoting, + grouping, a server-side row model for millions of rows — AG Grid + picks up where Table leaves off, across React, Angular, Vue, and + vanilla JavaScript. +

+ +

+ "Together we strive to educate the ecosystem about the differences + between the two libraries and when to choose which." — the TanStack + Table & AG Grid partnership +

+ +
+ + +
+

+ Community is free forever. No credit card required. +

+
+ + {/* Stats */} +
+ {[ + ['MIT', 'Community license'], + ['4', 'Frameworks supported'], + ['1M+', 'Rows with server-side model'], + ['45-day', 'AG Grid Studio trial'], + ].map(([value, label]) => ( +
+
{value}
+
+ {label} +
+
+ ))} +
+ + {/* Features */} +
+

+ What AG Grid brings to the table +

+

+ Everything in Community is free and MIT-licensed. Enterprise adds + the heavy-duty features teams reach for when a basic table UI is no + longer enough. +

+
+ {features.map(({ Icon, title, desc }) => ( + + +
{title}
+

+ {desc} +

+
+ ))} +
+
+ + {/* How it works */} +
+

+ From install to grid in 4 steps +

+

+ AG Grid is a client-side React component. Install Community, + register the modules, and render a grid — then reach for Enterprise + when you need it. +

+ +
+ {steps.map(({ num, title, code }) => ( + +
+ {num} +
+
+
{title}
+ + {code} + +
+
+ ))} +
+ +
+ + +
+ +
+ + +
+
+ + {/* When to choose which */} +
+

+ TanStack Table vs AG Grid — when to choose which +

+

+ The two libraries share a problem space but take opposite + approaches. This is the honest guidance at the heart of the + partnership. +

+ +
+ +
+ Reach for TanStack Table when… +
+
    + {chooseTanStackTable.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ +
+ Reach for AG Grid when… +
+
    + {chooseAgGrid.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+
+
+ + {/* Library fit */} +
+

+ Fits into your TanStack stack +

+

+ AG Grid slots alongside the TanStack libraries you already use — + from Table to Query to Start. +

+ +
+ {libDetails.map(({ label, desc }) => ( + +
+ + {label} +
+

+ {desc} +

+
+ ))} +
+
+ + {/* AG Grid Studio spotlight */} +
+
+ + + AG Grid Studio + +
+

+ Embedded analytics. Built to perform. +

+

+ AG Grid Studio adds drag-and-drop dashboards, charts, grids, and + filters to your app without building analytics from scratch. It + shares a theming engine with AG Grid, so everything matches out of + the box. +

+ +
+ {studioFeatures.map(({ title, desc }) => ( + +
{title}
+

+ {desc} +

+
+ ))} +
+ +
+ + +
+
+ + {/* Explore subpages */} +
+

+ Go deeper +

+

+ Detailed guides for getting started, comparing grids, and unlocking + Enterprise and Studio. +

+
+ +
+
+ + {/* FAQ */} +
+

+ Frequently asked questions +

+

+ Common questions from TanStack developers evaluating AG Grid. +

+ +
+ {faqs.map(({ q, a }, i) => { + const isOpen = openFaq === i + return ( + setOpenFaq(next ? i : null)} + className="border-b border-gray-200 dark:border-gray-800" + > + + + {q} + + + + +

+ {a} +

+
+
+ ) + })} +
+
+ + {/* CTA */} +
+
+ Official TanStack Table open-source partner +
+

+ Ready to build with AG Grid? +

+

+ Start free with Community, unlock Enterprise when you need it, and + add embedded dashboards with AG Grid Studio. +

+ +
+ + +
+
+ +

+ AG Grid is the official open-source partner of TanStack Table.{' '} + + Browse all TanStack partners + + .{' '} + trackAgGridClick()} + className="underline decoration-dotted underline-offset-2 hover:text-gray-700 dark:hover:text-gray-300" + > + ag-grid.com + +

+
+ +
+
+ ) +} diff --git a/src/routes/partners.ag-grid.studio.tsx b/src/routes/partners.ag-grid.studio.tsx new file mode 100644 index 000000000..69eb6e8e0 --- /dev/null +++ b/src/routes/partners.ag-grid.studio.tsx @@ -0,0 +1,410 @@ +import * as React from 'react' +import { createFileRoute } from '@tanstack/react-router' +import { + ArrowUpRight, + Boxes, + Gauge, + LayoutDashboard, + Palette, + Plus, + Sparkles, + SplitSquareVertical, +} from 'lucide-react' +import { twMerge } from 'tailwind-merge' +import { Footer } from '~/components/Footer' +import { Card } from '~/components/Card' +import { Button } from '~/ui' +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '~/components/Collapsible' +import { seo } from '~/utils/seo' +import { trackEvent } from '~/utils/analytics' +import { + AG_GRID_LINKS, + AgGridBreadcrumb, + AgGridCodeExample, + AgGridSubpageNav, + CheckBadge, + trackAgGridClick, +} from '~/components/AgGridPartner' + +const QUICKSTART_SNIPPET = `import { AgStudio } from 'ag-studio-react' + +// Each source is an id plus an array of plain row objects. +const sources = [ + { + id: 'sales', + data: [ + { region: 'EMEA', product: 'Grid', revenue: 128000 }, + { region: 'AMER', product: 'Charts', revenue: 96500 }, + { region: 'APAC', product: 'Studio', revenue: 71200 }, + ], + }, +] + +export function Dashboard() { + // Render inside a sized parent; "edit" mode lets users build reports. + return ( +
+ +
+ ) +} +` + +type FeatureIcon = React.ComponentType<{ className?: string }> + +const features: Array<{ Icon: FeatureIcon; title: string; desc: string }> = [ + { + Icon: LayoutDashboard, + title: 'Drag & drop widgets', + desc: 'Rearrange and resize charts, grids, and KPI tiles in real time. Build a dashboard by moving pieces, not writing layout code.', + }, + { + Icon: SplitSquareVertical, + title: 'Edit and view modes', + desc: 'Configure everything in edit mode, then hand a clean, read-only reporting view to stakeholders.', + }, + { + Icon: Gauge, + title: 'High-performance data', + desc: 'Process hundreds of thousands of rows in the browser with joins, aggregations, and computed columns.', + }, + { + Icon: Boxes, + title: 'Widget gallery', + desc: 'Pre-built bar, line, area, pie, and scatter charts, plus grids and KPI tiles, ready to drop into any dashboard.', + }, + { + Icon: Sparkles, + title: 'AI assistant', + desc: 'Explore your data in natural language and generate whole dashboards from a prompt.', + }, + { + Icon: Palette, + title: 'Shared theming', + desc: 'Studio shares a theming engine with AG Grid, so your dashboards and grids look like one product out of the box.', + }, +] + +const fitPoints: Array = [ + 'Embed dashboards inside a TanStack Start or Router app as a client-side React component', + 'Feed widgets from a TanStack Query-backed API, then let Studio handle joins and aggregations', + 'Reuse your AG Grid theme so embedded analytics match the rest of your product', + 'Give non-technical users self-service reporting without building analytics infrastructure', +] + +const faqs: Array<{ q: string; a: string }> = [ + { + q: 'How much does AG Grid Studio cost?', + a: 'AG Grid Studio requires a commercial license for production use. You can start with a 45-day free trial that includes full features with no watermarks or restrictions, plus direct engineering support during the trial.', + }, + { + q: 'How is Studio different from AG Grid Enterprise?', + a: 'AG Grid Enterprise is the data grid component with features like pivoting and the server-side row model. AG Grid Studio is a higher-level toolkit for building complete drag-and-drop analytics dashboards — charts, grids, filters, and KPI tiles — on top of AG Grid and AG Charts.', + }, + { + q: 'How much data can Studio handle?', + a: 'Studio is built for performance and processes hundreds of thousands of rows in the browser, with joins, aggregations, and computed columns. For very large datasets you can pre-aggregate on your backend and feed the results into Studio.', + }, + { + q: 'Does Studio work with React and TanStack?', + a: 'Yes. Studio supports React, Angular, Vue, and vanilla JavaScript, so it embeds inside a TanStack Start or Router app as a standard client-side component.', + }, +] + +const PAGE_TITLE = 'AG Grid Studio — Embedded Analytics for TanStack Apps' +const PAGE_DESCRIPTION = + 'AG Grid Studio is an embedded-analytics toolkit for building drag-and-drop dashboards on top of AG Grid and AG Charts. Widget gallery, AI assistant, edit and view modes, and a shared theming engine — with a 45-day free trial. Embed it in any TanStack Start or Router app.' + +function getFaqJsonLd() { + return { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: faqs.map((faq) => ({ + '@type': 'Question', + name: faq.q, + acceptedAnswer: { '@type': 'Answer', text: faq.a }, + })), + } +} + +export const Route = createFileRoute('/partners/ag-grid/studio')({ + head: () => ({ + meta: seo({ + title: PAGE_TITLE, + description: PAGE_DESCRIPTION, + keywords: + 'ag grid studio, embedded analytics react, dashboard toolkit, tanstack dashboards, ag charts, self-service reporting', + image: 'https://tanstack.com/og.png', + }), + scripts: [ + { + type: 'application/ld+json', + children: JSON.stringify(getFaqJsonLd()), + }, + ], + }), + component: AgGridStudioPage, +}) + +function AgGridStudioPage() { + const [openFaq, setOpenFaq] = React.useState(null) + + React.useEffect(() => { + trackEvent('partner_viewed', { + partner_id: 'ag-grid', + placement: 'detail', + }) + }, []) + + return ( +
+
+ + + {/* Hero */} +
+
+ + + AG Grid Studio + +
+

+ Embedded analytics. +
+ Built to perform. +

+

+ AG Grid Studio adds dashboards, charts, grids, and filters to your + app without building analytics from scratch. Drag, drop, and ship + self-service reporting your users can drive themselves. +

+ +
+ + +
+

+ Full features during the trial. No watermarks. Engineering support + included. +

+
+ + {/* Features */} +
+

+ Everything you need to build dashboards +

+

+ Studio is a complete toolkit for embedded analytics, powered by AG + Grid and{' '} + trackAgGridClick()} + className="underline decoration-dotted underline-offset-2 hover:text-blue-500" + > + AG Charts + {' '} + under the hood. +

+
+ {features.map(({ Icon, title, desc }) => ( + + +
{title}
+

+ {desc} +

+
+ ))} +
+
+ + {/* TanStack fit */} +
+

+ A natural fit for TanStack apps +

+

+ Studio is a client-side React component, so it drops into the apps + you already build with TanStack. +

+ +
    + {fitPoints.map((point) => ( +
  • + + {point} +
  • + ))} +
+
+
+ + {/* Quick start */} +
+

+ Render a dashboard in React +

+

+ Install{' '} + + ag-studio-react + + , hand{' '} + + AgStudio + {' '} + your data sources, and switch on edit mode so users can build + reports. +

+
+ +
+
+ + +
+
+ + {/* FAQ */} +
+

+ AG Grid Studio FAQ +

+
+ {faqs.map(({ q, a }, i) => { + const isOpen = openFaq === i + return ( + setOpenFaq(next ? i : null)} + className="border-b border-gray-200 dark:border-gray-800" + > + + + {q} + + + + +

+ {a} +

+
+
+ ) + })} +
+
+ + {/* CTA */} +
+

+ Ship analytics without the infrastructure +

+

+ Try AG Grid Studio free for 45 days with full features and no + watermarks. +

+
+ + +
+
+ + {/* Explore more */} +
+

+ Explore more +

+
+ +
+
+
+ +
+
+ ) +} diff --git a/src/routes/partners.ag-grid.tsx b/src/routes/partners.ag-grid.tsx new file mode 100644 index 000000000..ad684f398 --- /dev/null +++ b/src/routes/partners.ag-grid.tsx @@ -0,0 +1,9 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/partners/ag-grid')({ + component: AgGridLayout, +}) + +function AgGridLayout() { + return +} diff --git a/src/routes/partners.ag-grid.vs-tanstack-table.tsx b/src/routes/partners.ag-grid.vs-tanstack-table.tsx new file mode 100644 index 000000000..45a973ff6 --- /dev/null +++ b/src/routes/partners.ag-grid.vs-tanstack-table.tsx @@ -0,0 +1,317 @@ +import * as React from 'react' +import { createFileRoute } from '@tanstack/react-router' +import { ArrowUpRight, Plus } from 'lucide-react' +import { twMerge } from 'tailwind-merge' +import { Footer } from '~/components/Footer' +import { Card } from '~/components/Card' +import { Button } from '~/ui' +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '~/components/Collapsible' +import { seo } from '~/utils/seo' +import { trackEvent } from '~/utils/analytics' +import { + AG_GRID_LINKS, + AgGridBreadcrumb, + AgGridSubpageNav, + CheckBadge, + trackAgGridClick, +} from '~/components/AgGridPartner' + +const chooseTanStackTable: Array = [ + 'You want a headless grid — you own the markup, styles, and design system', + 'Bundle size and full DOM control matter more than built-in features', + 'You are composing a bespoke table UI, not shipping a spreadsheet replacement', + 'You want the same core across React, Vue, Solid, Svelte, Qwik, Angular, and Lit', + 'Your dataset fits comfortably in the client-side model (tens of thousands of rows)', + 'You are happy to wire up sorting, grouping, and virtualization yourself', +] + +const chooseAgGrid: Array = [ + 'You need enterprise features like pivoting or a server-side row model out of the box', + 'You are handling hundreds of thousands to millions of rows', + 'You want Excel export, integrated charts, and range selection without building them', + 'Master/detail, tree data, and clipboard support are requirements, not nice-to-haves', + 'You want a batteries-included grid your whole team can ship this week', + 'You need commercial support behind the grid at the center of your product', +] + +type Row = { feature: string; table: string; agGrid: string } + +const comparison: Array = [ + { + feature: 'Architecture', + table: 'Headless (bring your own UI)', + agGrid: 'Full component with built-in UI', + }, + { + feature: 'License', + table: 'MIT (free)', + agGrid: 'Community MIT · Enterprise commercial', + }, + { + feature: 'Styling', + table: 'Your CSS / design system', + agGrid: 'Theming engine with design tokens', + }, + { + feature: 'Row virtualization', + table: 'Pair with TanStack Virtual', + agGrid: 'Built in (rows and columns)', + }, + { + feature: 'Row grouping & pivoting', + table: 'Build it yourself', + agGrid: 'Enterprise, built in', + }, + { + feature: 'Server-side row model', + table: 'Build it yourself', + agGrid: 'Enterprise, built in', + }, + { + feature: 'Excel export & charts', + table: 'Bring your own', + agGrid: 'Enterprise, built in', + }, + { + feature: 'Best for', + table: 'Custom, lightweight tables', + agGrid: 'Large-scale enterprise data grids', + }, +] + +const faqs: Array<{ q: string; a: string }> = [ + { + q: 'Are AG Grid and TanStack Table competitors?', + a: 'No — they are partners. AG Grid is the official open-source partner of TanStack Table. They solve the same broad problem with opposite philosophies: TanStack Table is headless and composable, AG Grid is full-featured and batteries-included. Many teams use both in the same product.', + }, + { + q: 'Can I migrate from TanStack Table to AG Grid?', + a: 'Yes. Because both are column-and-row driven, your column definitions and row data map over cleanly. TanStack Table column defs become AG Grid columnDefs, and your data array becomes rowData. The main shift is that AG Grid renders the UI for you instead of you rendering it.', + }, + { + q: 'Which one is faster?', + a: 'It depends on the workload. TanStack Table is a thin logic layer, so raw overhead is minimal, but you supply the rendering and virtualization. AG Grid ships heavily optimized rendering, virtualization, and a server-side row model designed for very large datasets out of the box.', + }, + { + q: 'Can I use both in the same app?', + a: 'Absolutely. A common pattern is TanStack Table for lightweight, custom-styled tables and AG Grid for the heavy data-grid surfaces that need pivoting, grouping, or millions of rows.', + }, +] + +const PAGE_TITLE = 'AG Grid vs TanStack Table — When to Choose Which' +const PAGE_DESCRIPTION = + 'AG Grid and TanStack Table are partners, not rivals. TanStack Table is a headless, composable grid; AG Grid is a batteries-included enterprise data grid. Compare architecture, features, licensing, and scale to choose the right one — or use both.' + +function getFaqJsonLd() { + return { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: faqs.map((faq) => ({ + '@type': 'Question', + name: faq.q, + acceptedAnswer: { '@type': 'Answer', text: faq.a }, + })), + } +} + +export const Route = createFileRoute('/partners/ag-grid/vs-tanstack-table')({ + head: () => ({ + meta: seo({ + title: PAGE_TITLE, + description: PAGE_DESCRIPTION, + keywords: + 'ag grid vs tanstack table, tanstack table vs ag grid, react data grid comparison, headless table, enterprise data grid, react-table alternative', + image: 'https://tanstack.com/og.png', + }), + scripts: [ + { + type: 'application/ld+json', + children: JSON.stringify(getFaqJsonLd()), + }, + ], + }), + component: AgGridVsTablePage, +}) + +function AgGridVsTablePage() { + const [openFaq, setOpenFaq] = React.useState(null) + + React.useEffect(() => { + trackEvent('partner_viewed', { + partner_id: 'ag-grid', + placement: 'detail', + }) + }, []) + + return ( +
+
+ + + {/* Hero */} +
+ + Partners, not rivals + +

+ AG Grid vs TanStack Table +

+

+ Both libraries live in the same problem space but take opposite + approaches. TanStack Table is headless and composable; AG Grid is + full-featured and batteries-included. AG Grid is the official + open-source partner of TanStack Table — here is how to pick the + right one, or use both together. +

+
+ + {/* When to choose which */} +
+
+ +
+ Reach for TanStack Table when… +
+
    + {chooseTanStackTable.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+ +
+ Reach for AG Grid when… +
+
    + {chooseAgGrid.map((item) => ( +
  • + + {item} +
  • + ))} +
+
+
+
+ + {/* Side-by-side table */} +
+

+ Side by side +

+

+ A quick reference across the dimensions teams weigh most. +

+
+ + + + + + + + + {comparison.map(({ feature, table, agGrid }) => ( + + + + + + ))} + +
+ TanStack TableAG Grid
+ {feature} + + {table} + + {agGrid} +
+
+ +
+ + +
+
+ + {/* FAQ */} +
+

+ Common questions +

+
+ {faqs.map(({ q, a }, i) => { + const isOpen = openFaq === i + return ( + setOpenFaq(next ? i : null)} + className="border-b border-gray-200 dark:border-gray-800" + > + + + {q} + + + + +

+ {a} +

+
+
+ ) + })} +
+
+ + {/* Explore more */} +
+

+ Explore more +

+
+ +
+
+
+ +
+ ) +}