-
-
Notifications
You must be signed in to change notification settings - Fork 372
Expand file tree
/
Copy pathpartners.tsx
More file actions
1766 lines (1624 loc) · 53.8 KB
/
Copy pathpartners.tsx
File metadata and controls
1766 lines (1624 loc) · 53.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import agGridDarkSvg from '~/images/ag-grid-dark.svg'
import agGridLightSvg from '~/images/ag-grid-light.svg'
import nozzleImage from '~/images/nozzle.png'
import bytesFireshipImage from '~/images/bytes-fireship.png'
import vercelLightSvg from '~/images/vercel-light.svg'
import vercelDarkSvg from '~/images/vercel-dark.svg'
import netlifyLightSvg from '~/images/netlify-light.svg'
import netlifyDarkSvg from '~/images/netlify-dark.svg'
import lovableBlackSvg from '~/images/lovable-black.svg'
import lovableWhiteSvg from '~/images/lovable-white.svg'
import convexWhiteSvg from '~/images/convex-white.svg'
import convexColorSvg from '~/images/convex-color.svg'
import clerkLightSvg from '~/images/clerk-logo-light.svg'
import clerkDarkSvg from '~/images/clerk-logo-dark.svg'
import sentryWordMarkLightSvg from '~/images/sentry-wordmark-light.svg'
import sentryWordMarkDarkSvg from '~/images/sentry-wordmark-dark.svg'
import speakeasyLightSvg from '~/images/speakeasy-light.svg'
import speakeasyDarkSvg from '~/images/speakeasy-dark.svg'
import neonLightSvg from '~/images/neon-light.svg'
import neonDarkSvg from '~/images/neon-dark.svg'
import unkeyBlackSvg from '~/images/unkey-black.svg'
import unkeyWhiteSvg from '~/images/unkey-white.svg'
import electricDarkSvg from '~/images/electric-dark.svg'
import electricLightSvg from '~/images/electric-light.svg'
import prismaLightSvg from '~/images/prisma-light.svg'
import prismaDarkSvg from '~/images/prisma-dark.svg'
import codeRabbitLightSvg from '~/images/coderabbit-light.svg'
import codeRabbitDarkSvg from '~/images/coderabbit-dark.svg'
import strapiLightSvg from '~/images/strapi-light.svg'
import strapiDarkSvg from '~/images/strapi-dark.svg'
import serpapiWhiteSvg from '~/images/serpapi-white.svg'
import serpapiBlackSvg from '~/images/serpapi-black.svg'
import { libraries, type Library } from '~/libraries'
import cloudflareWhiteSvg from '~/images/cloudflare-white.svg'
import cloudflareBlackSvg from '~/images/cloudflare-black.svg'
import workosBlackSvg from '~/images/workos-black.svg'
import workosWhiteSvg from '~/images/workos-white.svg'
import powersyncBlackSvg from '~/images/powersync-black.svg'
import powersyncWhiteSvg from '~/images/powersync-white.svg'
import railwayBlackSvg from '~/images/railway-black.svg'
import railwayWhiteSvg from '~/images/railway-white.svg'
import openrouterBlackSvg from '~/images/openrouter-black.svg'
import openrouterWhiteSvg from '~/images/openrouter-white.svg'
import {
getPartnerPlacementContext,
getPartnersForPlacement,
type PartnerPlacementContext,
} from '~/utils/partner-placement'
function LearnMoreButton() {
return (
<span className="text-blue-500 uppercase font-black text-sm">
Learn More
</span>
)
}
type PartnerImageConfig =
| { light: string; dark: string; scale?: number }
| { src: string; scale?: number }
type PartnerApplicationStarterIcon = {
mode: 'contain' | 'left-crop'
src: string
}
type ApplicationStarterPartnerTier = 1 | 2 | 3
export const partnerUniqueConstraints = ['auth-provider', 'hosting'] as const
export type PartnerUniqueConstraint = (typeof partnerUniqueConstraints)[number]
export const partnerTiers = ['gold', 'silver', 'bronze'] as const
export type PartnerTier = (typeof partnerTiers)[number]
export const partnerTierLabels: Record<PartnerTier, string> = {
gold: 'Gold',
silver: 'Silver',
bronze: 'Bronze',
}
export const partnerTierOrder: Record<PartnerTier, number> = {
gold: 0,
silver: 1,
bronze: 2,
}
const partnerTierToBuilderTier: Record<
PartnerTier,
ApplicationStarterPartnerTier
> = {
gold: 1,
silver: 2,
bronze: 3,
}
export const partnerTierFlares: Record<
PartnerTier,
{
gradientStops: string
iconColor: string
labelColor: string
icon: React.ReactNode
}
> = {
gold: {
gradientStops:
'from-yellow-400 via-amber-500 to-orange-600 dark:from-yellow-300 dark:via-amber-400 dark:to-orange-400',
iconColor: 'text-amber-500 dark:text-amber-300',
labelColor: 'text-amber-600 dark:text-amber-300',
// 5-point star
icon: (
<svg viewBox="0 0 12 12" className="h-3 w-3 fill-current" aria-hidden>
<path d="M6 0.5 L7.5 4.2 L11.5 4.6 L8.5 7.2 L9.4 11.2 L6 9.2 L2.6 11.2 L3.5 7.2 L0.5 4.6 L4.5 4.2 Z" />
</svg>
),
},
silver: {
gradientStops:
'from-slate-200 via-zinc-400 to-slate-300 dark:from-slate-300 dark:via-zinc-400 dark:to-slate-400',
iconColor: 'text-slate-400 dark:text-slate-300',
labelColor: 'text-slate-500 dark:text-slate-300',
// 4-point sparkle
icon: (
<svg viewBox="0 0 12 12" className="h-3 w-3 fill-current" aria-hidden>
<path d="M6 0 L7.2 4.8 L12 6 L7.2 7.2 L6 12 L4.8 7.2 L0 6 L4.8 4.8 Z" />
</svg>
),
},
bronze: {
gradientStops:
'from-amber-700 via-amber-800 to-amber-950 dark:from-amber-600 dark:via-amber-800 dark:to-amber-950',
iconColor: 'text-amber-800 dark:text-amber-600',
labelColor: 'text-amber-800 dark:text-amber-600',
// diamond
icon: (
<svg viewBox="0 0 12 12" className="h-3 w-3 fill-current" aria-hidden>
<path d="M6 0.5 L11.5 6 L6 11.5 L0.5 6 Z" />
</svg>
),
},
}
export function PartnerImage({
className,
config,
alt,
}: {
className?: string
config: PartnerImageConfig
alt: string
}) {
const scaleStyle = config.scale ? { transform: `scale(${config.scale})` } : {}
if ('light' in config && 'dark' in config) {
return (
<div
className="w-full flex items-center justify-center"
style={scaleStyle}
>
<img
src={config.light}
alt={alt}
loading="lazy"
className={
className ? `${className} dark:hidden` : 'w-full dark:hidden'
}
width={200}
height={100}
sizes="(max-width: 640px) 80px, (max-width: 1024px) 150px, 200px"
/>
<img
src={config.dark}
alt={alt}
loading="lazy"
className={
className
? `${className} hidden dark:block`
: 'w-full hidden dark:block'
}
width={200}
height={100}
sizes="(max-width: 640px) 80px, (max-width: 1024px) 150px, 200px"
/>
</div>
)
}
return (
<div className="w-full flex items-center justify-center" style={scaleStyle}>
<img
src={config.src}
alt={alt}
className={className ?? 'w-full'}
width={200}
height={100}
loading="lazy"
sizes="(max-width: 640px) 80px, (max-width: 1024px) 150px, 200px"
/>
</div>
)
}
export const partnerCategories = [
'code-review',
'deployment',
'data-grid',
'auth',
'database',
'monitoring',
'cms',
'api',
'ai',
'learning',
] as const
type PartnerCategory = (typeof partnerCategories)[number]
export const partnerCategoryLabels: Record<PartnerCategory, string> = {
'code-review': 'Code Review',
deployment: 'Deployment/Hosting',
'data-grid': 'Data Grids',
auth: 'Authentication',
database: 'Databases',
monitoring: 'Error Monitoring',
cms: 'CMS',
api: 'API Management',
ai: 'AI/LLM',
learning: 'Learning Resources',
}
export type Partner = {
applicationStarterPromptInstructions?: Array<string>
name: string
id: string
libraries?: Library['id'][]
href: string
applicationStarterIcon?: PartnerApplicationStarterIcon
image: PartnerImageConfig
content: JSX.Element
llmDescription: string
category: PartnerCategory
status?: 'active' | 'inactive'
startDate?: string
endDate?: string
score: number
uniqueConstraints?: Array<PartnerUniqueConstraint>
tier?: PartnerTier
brandColor?: string // Primary brand color for game elements
tagline?: string // Short tagline for game info cards
}
export type ApplicationStarterPartnerSuggestion = {
brandColor?: Partner['brandColor']
description: string
hint: string
iconMode?: 'contain' | 'left-crop'
id: string
iconSrc?: string
image: Partner['image']
label: string
tags: Array<string>
tier: ApplicationStarterPartnerTier
uniqueConstraints: Array<PartnerUniqueConstraint>
}
const APPLICATION_STARTER_GUIDANCE_MARKER = 'Starter guidance:'
const APPLICATION_STARTER_SELECTED_PARTNERS_MARKER = 'Selected partner ids:'
const APPLICATION_STARTER_INFERRED_PARTNERS_MARKER = 'Inferred partner ids:'
const APPLICATION_STARTER_FORCE_ROUTER_ONLY_MARKER = 'Force router-only: true'
export function getApplicationStarterUserBrief(input: string) {
const [brief] = input.split(`\n\n${APPLICATION_STARTER_GUIDANCE_MARKER}\n`)
return brief?.trim() ?? ''
}
export function getApplicationStarterGuidanceLines(input: string) {
const [, guidance] = input.split(
`\n\n${APPLICATION_STARTER_GUIDANCE_MARKER}\n`,
)
if (!guidance) {
return []
}
return guidance
.split('\n')
.map((line) => line.trim())
.filter(
(line) =>
!line.startsWith(APPLICATION_STARTER_SELECTED_PARTNERS_MARKER) &&
!line.startsWith(APPLICATION_STARTER_INFERRED_PARTNERS_MARKER) &&
line !== APPLICATION_STARTER_FORCE_ROUTER_ONLY_MARKER,
)
.filter(Boolean)
}
export function getApplicationStarterForceRouterOnly(input: string) {
return input.includes(APPLICATION_STARTER_FORCE_ROUTER_ONLY_MARKER)
}
function extractApplicationStarterPartnerIdsFromMarker({
input,
marker,
}: {
input: string
marker: string
}) {
const escapedMarker = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const pattern = new RegExp(`${escapedMarker}\\s*([^\\n]+)`, 'i')
const value = input.match(pattern)?.[1]?.trim()
if (!value) {
return []
}
return value
.split(',')
.map((part) => part.trim())
.filter(Boolean)
}
export function getApplicationStarterSelectedPartnerIds(input: string) {
return extractApplicationStarterPartnerIdsFromMarker({
input,
marker: APPLICATION_STARTER_SELECTED_PARTNERS_MARKER,
})
}
export function getApplicationStarterInferredPartnerIds(input: string) {
return extractApplicationStarterPartnerIdsFromMarker({
input,
marker: APPLICATION_STARTER_INFERRED_PARTNERS_MARKER,
})
}
const neon = (() => {
const href = 'https://neon.tech?utm_source=tanstack'
return {
name: 'Neon',
id: 'neon',
libraries: ['start', 'router'],
status: 'inactive' as const,
endDate: 'Apr 2026',
score: 0.297,
href,
brandColor: '#00E599',
tagline: 'Serverless Postgres',
image: {
light: neonLightSvg,
dark: neonDarkSvg,
},
llmDescription:
'Serverless Postgres platform with branching, autoscaling compute, and separate storage and compute. Neon also publishes a TanStack Start setup prompt and docs for getting TanStack apps running on Postgres quickly.',
category: 'database',
content: (
<>
<div className="text-xs">
Neon provides <strong>serverless Postgres</strong> with branching,
autoscaling compute, and separate storage and compute. That makes it
especially useful for preview environments, branch-based workflows,
and fast iteration with TanStack Start.
</div>
<LearnMoreButton />
</>
),
}
})()
const convex = (() => {
const href = 'https://convex.dev?utm_source=tanstack'
return {
name: 'Convex',
id: 'convex',
libraries: ['start', 'router'],
status: 'inactive' as const,
startDate: 'May 2024',
endDate: 'Mar 2026',
score: 0.286,
href,
brandColor: '#F3A712',
tagline: 'Real-time Database',
image: {
light: convexColorSvg,
dark: convexWhiteSvg,
},
llmDescription:
'Reactive backend platform with a document database, relational data model, TypeScript functions, and realtime query updates. Convex also has an official TanStack Start quickstart.',
category: 'database',
content: (
<>
<div className="text-xs">
Convex provides a <strong>reactive backend</strong> with TypeScript
queries, mutations, and realtime updates to clients. For TanStack
apps, it is a useful option when you want live data behavior without
wiring your own sync layer by hand.
</div>
<LearnMoreButton />
</>
),
}
})()
const clerk = (() => {
const href = 'https://go.clerk.com/wOwHtuJ'
return {
name: 'Clerk',
id: 'clerk',
href,
libraries: ['start', 'router'],
status: 'active' as const,
score: 0.286,
tier: 'silver' as const,
uniqueConstraints: [
'auth-provider',
] satisfies Array<PartnerUniqueConstraint>,
brandColor: '#6C47FF',
tagline: 'Authentication',
image: {
light: clerkLightSvg,
dark: clerkDarkSvg,
scale: 0.72,
},
llmDescription:
'Authentication and user management platform with prebuilt UI, sessions, organizations, and MFA. Clerk has official SDKs and quickstarts for TanStack React Start and React Router.',
category: 'auth',
content: (
<>
<div className="text-xs">
Clerk provides <strong>authentication and user management</strong>
with prebuilt UI, sessions, organizations, and MFA. It also publishes
official guides for{' '}
<strong>TanStack React Start and React Router</strong>, which makes
integration straightforward.
</div>
<LearnMoreButton />
</>
),
}
})()
const workos = (() => {
const href = 'https://workos.com?utm_source=tanstack'
return {
name: 'WorkOS',
id: 'workos',
href,
libraries: ['start', 'router'] as const,
status: 'active' as const,
score: 0.314,
tier: 'silver' as const,
uniqueConstraints: [
'auth-provider',
] satisfies Array<PartnerUniqueConstraint>,
brandColor: '#6363F1',
tagline: 'Enterprise Auth',
applicationStarterIcon: {
mode: 'left-crop',
src: workosBlackSvg,
},
image: {
light: workosBlackSvg,
dark: workosWhiteSvg,
},
llmDescription:
'Enterprise identity platform with SSO, Directory Sync, MFA for AuthKit, RBAC, audit logs, and admin onboarding tools. It is a strong fit for B2B apps that need enterprise auth features.',
category: 'auth',
content: (
<>
<div className="text-xs">
WorkOS focuses on <strong>enterprise identity</strong> features like
SSO, Directory Sync, RBAC, audit logs, and admin onboarding, plus MFA
in AuthKit. That makes it a practical fit for B2B TanStack apps with
organization-level access requirements.
</div>
<LearnMoreButton />
</>
),
}
})()
const agGrid = (() => {
const href =
'https://ag-grid.com/react-data-grid/?utm_source=reacttable&utm_campaign=githubreacttable'
return {
name: 'AG Grid',
id: 'ag-grid',
libraries: ['table'] as const,
status: 'active' as const,
score: 0.497,
tier: 'silver' as const,
href,
brandColor: '#FF8C00',
tagline: 'Enterprise Data Grid',
applicationStarterIcon: {
mode: 'contain',
src: 'https://www.ag-grid.com/_astro/favicon-32.WDuB-104.png',
},
applicationStarterPromptInstructions: [
'Install ag-grid-react and ag-grid-community and use AG Grid Community by default for a real working demo.',
'Only add ag-grid-enterprise if a license key is explicitly provided or explicitly requested.',
'Render a real grid with explicit columns, row data, and a container height so the integration is visibly demonstrated in the app.',
],
image: {
light: agGridDarkSvg,
dark: agGridLightSvg,
scale: 1.1,
},
llmDescription:
'Data grid library with Community and Enterprise editions. Enterprise features include row grouping, pivoting, aggregation, Excel export, integrated charts, and the server-side row model.',
category: 'data-grid',
content: (
<>
<div className="text-xs">
AG Grid covers the heavier end of the grid spectrum. Its
<strong> Enterprise</strong> offering adds row grouping, pivoting,
aggregation, Excel export, integrated charts, and a server-side row
model when a basic table UI is not enough.
</div>
{/* Has to be button for separate link than parent anchor to be valid HTML */}
<button
type="button"
onClick={() => {
window.location.href = '/blog/ag-grid-partnership'
}}
className="text-blue-500 uppercase font-black text-sm"
>
Learn More
</button>
</>
),
}
})()
const netlify = (() => {
const href = 'https://netlify.com?utm_source=tanstack'
return {
name: 'Netlify',
id: 'netlify',
libraries: ['start', 'router'],
status: 'active' as const,
score: 0.343,
tier: 'silver' as const,
uniqueConstraints: ['hosting'] satisfies Array<PartnerUniqueConstraint>,
href,
brandColor: '#00C7B7',
tagline: 'Web Deployment',
applicationStarterIcon: {
mode: 'contain',
src: 'https://www.netlify.com/favicon/icon.svg',
},
image: {
light: netlifyLightSvg,
dark: netlifyDarkSvg,
scale: 1.25,
},
llmDescription:
'Deployment platform for web applications with Deploy Previews, Functions, Edge Functions, and an official TanStack Start integration guide.',
category: 'deployment',
content: (
<>
<div className="text-xs">
Netlify provides <strong>Deploy Previews</strong>, Functions, Edge
Functions, and a concrete TanStack Start integration path. That makes
it useful when the deployment workflow is part of the product
workflow, not an afterthought.
</div>
<LearnMoreButton />
</>
),
}
})()
const cloudflare = (() => {
const href = 'https://www.cloudflare.com?utm_source=tanstack'
return {
name: 'Cloudflare',
id: 'cloudflare',
href,
// Show on every repo
libraries: libraries.map((l) => l.id),
status: 'active' as const,
score: 0.857,
tier: 'gold' as const,
uniqueConstraints: ['hosting'] satisfies Array<PartnerUniqueConstraint>,
startDate: 'Sep 2025',
brandColor: '#F6821F',
tagline: 'Edge Deployment',
image: {
light: cloudflareBlackSvg,
dark: cloudflareWhiteSvg,
},
llmDescription:
'Global network and developer platform with Workers, KV, CDN, and security services. Cloudflare also documents deploying TanStack Start apps on Workers.',
category: 'deployment',
content: (
<>
<div className="text-xs">
Cloudflare combines <strong>Workers</strong>, KV, CDN, and security
services on a global network. It also documents deploying TanStack
Start apps on Workers, including bindings and prerendering support.
</div>
<LearnMoreButton />
</>
),
}
})()
const lovable = (() => {
const href = 'https://lovable.dev?utm_source=tanstack'
return {
name: 'Lovable',
id: 'lovable',
href,
libraries: ['start', 'router'] as const,
status: 'active' as const,
score: 0.714,
tier: 'gold' as const,
uniqueConstraints: ['hosting'] satisfies Array<PartnerUniqueConstraint>,
brandColor: '#FF7EB0',
tagline: 'AI App Builder',
applicationStarterPromptInstructions: [
'Treat Lovable as the AI app-building and hosting path, not as a TanStack CLI deployment flag or npm package.',
'Keep the generated app portable: start with the TanStack CLI output, preserve GitHub/project ownership notes, and call out any Lovable Cloud setup that cannot be automated from code.',
'When Lovable is selected, do not add a separate Cloudflare, Netlify, or Railway deployment target unless the user explicitly asks for a handoff path.',
],
image: {
light: lovableBlackSvg,
dark: lovableWhiteSvg,
},
llmDescription:
'AI app-building platform for generating, editing, and shipping web apps from prompts, with GitHub sync, visual editing, Lovable Cloud hosting, and new TanStack Start-powered SSR projects.',
category: 'ai',
content: (
<>
<div className="text-xs">
Lovable helps teams move from prompt to working app with{' '}
<strong>AI-assisted building</strong>, visual editing, GitHub sync,
and Lovable Cloud hosting. New Lovable projects are powered by
TanStack Start, which makes it especially relevant for teams that want
generated apps to keep strong routing, SSR, and type-safety
foundations.
</div>
<LearnMoreButton />
</>
),
}
})()
const sentry = (() => {
const href = 'https://sentry.io?utm_source=tanstack'
return {
name: 'Sentry',
id: 'sentry',
libraries: ['start', 'router'],
status: 'active' as const,
score: 0.229,
tier: 'bronze' as const,
href,
brandColor: '#362D59',
tagline: 'Error Monitoring',
image: {
light: sentryWordMarkDarkSvg,
dark: sentryWordMarkLightSvg,
},
llmDescription:
'Application monitoring platform for error tracking, tracing, replay, profiling, and logs. It also offers TanStack Router integration and an alpha TanStack Start React SDK.',
category: 'monitoring',
content: (
<>
<div className="text-xs">
Sentry goes beyond basic error collection with{' '}
<strong>tracing</strong>, replay, profiling, and logs. For TanStack
apps, that makes it easier to debug issues across client behavior,
routing, and performance.
</div>
<LearnMoreButton />
</>
),
}
})()
const fireship = (() => {
const href = 'https://bytes.dev?utm_source-tanstack&utm_campaign=tanstack'
return {
name: 'Fireship',
id: 'fireship',
libraries: [],
status: 'inactive' as const,
score: 0.014,
href,
tagline: 'Dev Education',
image: {
src: bytesFireshipImage,
},
llmDescription:
'Developer education brand behind Fireship courses and content, plus the Bytes JavaScript newsletter. Useful for staying current on web development and ecosystem trends.',
category: 'learning',
content: (
<>
<div className="text-xs">
Fireship produces developer education and Bytes publishes a JavaScript
newsletter. Together they are useful for helping more developers stay
current on web tooling, patterns, and ecosystem changes. Learn more
about{' '}
{/* Has to be button for separate link than parent anchor to be valid HTML */}
<button
type="button"
className="text-blue-500 underline cursor-pointer p-0 m-0 bg-transparent border-none inline"
onClick={() =>
window.open(
'https://fireship.dev/?utm_source=tanstack&utm_campaign=tanstack',
'_blank',
'noopener,noreferrer',
)
}
tabIndex={0}
>
Fireship
</button>{' '}
and{' '}
<button
type="button"
className="text-blue-500 underline cursor-pointer p-0 m-0 bg-transparent border-none inline"
onClick={() => window.open(href, '_blank', 'noopener,noreferrer')}
tabIndex={0}
>
Bytes.dev
</button>
.
</div>
<LearnMoreButton />
</>
),
}
})()
const nozzle = (() => {
const href = 'https://nozzle.io/?utm_source=tanstack&utm_campaign=tanstack'
return {
name: 'Nozzle.io',
id: 'nozzle',
href,
status: 'inactive' as const,
score: 0.014,
tagline: 'Enterprise SEO',
image: {
src: nozzleImage,
},
llmDescription:
'Enterprise keyword rank tracking and SERP monitoring platform with large-scale SEO reporting, share-of-voice analysis, historical data, and export capabilities.',
category: 'learning',
content: (
<>
<div className="text-xs">
Nozzle is an <strong>enterprise SEO</strong> product focused on rank
tracking, share-of-voice reporting, historical SERP data, and large-
scale exports. It remains relevant here because it represents a
demanding, data-heavy product category with serious analytics needs.
</div>
<LearnMoreButton />
</>
),
}
})()
const speakeasy = (() => {
const href =
'https://www.speakeasy.com/product/react-query?utm_source=tanstack&utm_campaign=tanstack'
return {
name: 'Speakeasy',
id: 'speakeasy',
href,
libraries: ['query'] as const,
status: 'inactive' as const,
startDate: 'Feb 2025',
endDate: 'Jul 2025',
image: {
light: speakeasyLightSvg,
dark: speakeasyDarkSvg,
},
llmDescription:
'API tooling focused on generating idiomatic SDKs, CLIs, Terraform providers, and hosted MCP servers from OpenAPI specs.',
category: 'api',
content: (
<>
<div className="text-xs">
Speakeasy focuses on generating{' '}
<strong>
SDKs, CLIs, Terraform providers, and hosted MCP servers
</strong>{' '}
from OpenAPI specs. That is useful when TanStack frontends depend on
well-generated client libraries instead of hand-maintained API code.
</div>
<LearnMoreButton />
</>
),
}
})()
const unkey = (() => {
const href = 'https://www.unkey.com/?utm_source=tanstack'
return {
name: 'Unkey',
id: 'unkey',
libraries: ['pacer'] as const,
status: 'active' as const,
score: 0.051,
tier: 'bronze' as const,
href,
brandColor: '#222222',
tagline: 'API Key Management',
applicationStarterPromptInstructions: [
'Use Unkey server-side only and never expose root keys or management credentials in client code.',
'Choose one concrete integration path based on the app: API key verification with @unkey/api or endpoint rate limiting with @unkey/ratelimit.',
'If the product need is unclear, prefer a minimal server-side TODO or example wrapper instead of inventing both paths at once.',
],
image: {
light: unkeyBlackSvg,
dark: unkeyWhiteSvg,
scale: 0.7,
},
llmDescription:
'API infrastructure for API key management, rate limiting, usage tracking, audit logs, and access controls.',
category: 'api',
content: (
<>
<div className="text-xs">
Unkey provides <strong>API key management</strong>, rate limiting,
usage tracking, audit logs, and access controls. That makes it a
practical fit for TanStack-built products that expose APIs and need
straightforward platform controls.
</div>
<LearnMoreButton />
</>
),
}
})()
const serpApi = (() => {
const href = 'https://serpapi.com?utm_source=tanstack'
return {
name: 'SerpAPI',
id: 'serpapi',
libraries: libraries.map((l) => l.id),
status: 'active' as const,
score: 0.41,
tier: 'silver' as const,
href,
brandColor: '#6361EC',
tagline: 'Real-time SERP API',
applicationStarterPromptInstructions: [
'Install the official serpapi package and keep all SerpApi usage server-side behind an app-owned endpoint or server function.',
'Read SERPAPI_API_KEY from environment variables and choose one explicit search engine instead of pretending to support every engine at once.',
'Normalize the response into app-owned data types before sending it to the UI.',
],
image: {
light: serpapiBlackSvg,
dark: serpapiWhiteSvg,
scale: 0.92,
},
llmDescription:
'Search engine results API with structured JSON output, geo-targeting controls, CAPTCHA solving, and support for Google, Maps, Shopping, News, and other engines.',
category: 'api',
content: (
<>
<div className="text-xs">
SerpApi handles search-engine access and returns structured results
for Google and other engines, with <strong>geo-targeting</strong>,
proxies, and CAPTCHA solving handled for you. That is useful for SEO
products, search intelligence, and AI workflows built with TanStack.
</div>
<LearnMoreButton />
</>
),
}
})()
const electric = (() => {
const href = 'https://electric-sql.com'
return {
name: 'Electric',
id: 'electric',
libraries: ['db'] as const,
status: 'active' as const,
score: 0.283,
tier: 'bronze' as const,
href,
brandColor: '#7e78db',
tagline: 'Sync Engine',
applicationStarterPromptInstructions: [
'Treat Electric as a platform-level integration, not a small drop-in add-on.',
'Do not hand-roll full sync plumbing unless the prompt explicitly asks for that; prefer clear setup notes or TODOs that point to the official Electric starter path.',
'If you demonstrate Electric at all, keep it to thin scaffolding and make missing local tooling or service setup explicit.',
],
image: {
light: electricLightSvg,
dark: electricDarkSvg,
},
llmDescription:
'Sync-focused data platform for Postgres-backed apps, including Postgres Sync and the partnered TanStack DB project. It is built for reactive and collaborative applications that need live data sync.',
category: 'database',
content: (
<>
<div className="text-xs">
Electric is focused on{' '}
<strong>sync and reactive data delivery</strong>
for Postgres-backed apps, including Postgres Sync and its work with
TanStack DB. It is especially relevant for collaborative or local-
first apps where live data movement is part of the core product.
</div>
<LearnMoreButton />
</>
),
}
})()
const vercel = (() => {
const href = 'https://vercel.com?utm_source=tanstack'
return {
name: 'Vercel',
id: 'vercel',
href,
libraries: ['start', 'router'] as const,
status: 'inactive' as const,
startDate: 'May 2024',
endDate: 'Oct 2024',
uniqueConstraints: ['hosting'] satisfies Array<PartnerUniqueConstraint>,
image: {
light: vercelLightSvg,
dark: vercelDarkSvg,
},
llmDescription:
'Cloud platform for deploying and scaling web applications with Git-based workflows, preview environments, global delivery, and Vercel Functions.',
category: 'deployment',
content: (
<>
<div className="text-xs">
Vercel provides <strong>Git-based deployments</strong>, preview
environments, global delivery, and server-side compute through Vercel
Functions. That makes it a familiar deployment option for TanStack
Start and Router teams building full-stack apps.
</div>
<LearnMoreButton />
</>
),
}
})()
const prisma = (() => {
const href = 'https://www.prisma.io/?utm_source=tanstack&via=tanstack'
return {
name: 'Prisma',
id: 'prisma',
href,
status: 'active' as const,
libraries: ['db', 'start'] as const,
startDate: 'Aug 2025',
score: 0.143,
tier: 'bronze' as const,
brandColor: '#2D3748',
tagline: 'Database ORM',
image: {
light: prismaLightSvg,
dark: prismaDarkSvg,
},
llmDescription:
'Open-source TypeScript ORM with Prisma Client, migrations, Prisma Studio, and Prisma Postgres for managed PostgreSQL.',
category: 'database',
content: (
<>
<div className="text-xs">
Prisma combines <strong>type-safe database access</strong>,
migrations, Prisma Studio, and Prisma Postgres into one workflow. It
also publishes a TanStack Start guide, which makes it a practical
option for full-stack apps that want a polished database layer.
</div>
<LearnMoreButton />
</>
),
}
})()
const codeRabbit = (() => {