Our Tech Stack in 2026: Every Tool We Use and Why
Every few months someone asks us what we use to build things. Usually it is a founder evaluating studios, sometimes another developer curious about our setup. Instead of repeating ourselves in calls, here is the full answer — every framework, service, and tool we rely on in 2026, why we picked it, and what we considered before settling.
We are a four-person studio. We do not have the luxury of maintaining six different stacks. Every tool earns its place by being reliable, productive, and something all four of us can work in without context-switching overhead.
Frontend: Astro and SvelteKit
We split our frontend work into two categories: content-driven sites and interactive applications.
For marketing sites, blogs, landing pages, and documentation — anything where content is king — we use Astro. This site runs on Astro. So does Spots Mexico, where we needed to generate thousands of SEO-optimized location pages at build time. Astro ships zero JavaScript by default and lets us sprinkle in interactivity only where we need it. The island architecture is not just a buzzword; it genuinely keeps pages fast without sacrificing developer experience.
For full applications — dashboards, CRMs, booking systems, anything with heavy client-side state — we use SvelteKit. We built MindHyv, Vincelio, and LancerSpace on SvelteKit. The reasons are straightforward: compiled output with no virtual DOM overhead, genuinely reactive state management without a library, and server-side rendering that works well out of the box.
We evaluated Next.js and Nuxt seriously. Next.js is powerful but the React ecosystem carries weight — state management libraries, hook conventions, re-render debugging. SvelteKit gives us the same capabilities with less code and fewer footguns. For a small team, that matters.
// SvelteKit load function — server-side data fetching is clean
export const load: PageServerLoad = async ({ params, locals }) => {
const { supabase, session } = locals;
const { data: project } = await supabase
.from('projects')
.select('*, client:clients(*)')
.eq('id', params.id)
.eq('team_id', session.user.team_id)
.single();
if (!project) throw error(404, 'Project not found');
return { project };
};

Mobile: Flutter
Every mobile project we take on ships in Flutter. We considered React Native, and we revisit that decision annually. Flutter wins for us because of consistent rendering across platforms, the widget system that makes complex UI composition predictable, and Dart — which is a boring, productive language that stays out of the way.
We used Flutter for the mobile components of MindHyv and Just The Rip. Hot reload alone saves us hours per week during UI iteration.
The tradeoff is ecosystem size. The React Native package ecosystem is larger. But we have found that the packages we actually need — camera, payments, push notifications, maps — are well-supported in Flutter. We are not building apps that need obscure native bridges.
Backend and Database: Supabase and PostgreSQL
Supabase is our default backend for every new project. We use it for authentication, database (PostgreSQL), real-time subscriptions, storage, and edge functions.
The honest reason: it eliminates an enormous amount of boilerplate. Auth with row-level security, real-time listeners, file uploads with transformation — these are table-stakes features that used to take weeks to set up properly. Supabase gives us all of that with a consistent API.
We lean heavily on PostgreSQL features that Supabase exposes well: row-level security policies, database functions, triggers, and generated columns.
-- Row-level security policy we use across most projects
-- Users can only access data belonging to their team
CREATE POLICY "team_isolation" ON projects
FOR ALL
USING (team_id = (SELECT team_id FROM profiles WHERE id = auth.uid()))
WITH CHECK (team_id = (SELECT team_id FROM profiles WHERE id = auth.uid()));
-- Database function for atomic operations
-- Used in Just The Rip for pack opening transactions
CREATE OR REPLACE FUNCTION open_pack(p_pack_id uuid, p_user_id uuid)
RETURNS jsonb AS $$
DECLARE
v_cards jsonb;
v_pack record;
BEGIN
SELECT * INTO v_pack FROM packs WHERE id = p_pack_id AND status = 'sealed'
FOR UPDATE;
IF NOT FOUND THEN
RAISE EXCEPTION 'Pack not available';
END IF;
-- Determine cards using weighted random selection
SELECT jsonb_agg(card) INTO v_cards
FROM select_random_cards(v_pack.set_id, v_pack.card_count);
-- Update pack status and assign to user
UPDATE packs SET status = 'opened', opened_by = p_user_id, opened_at = now()
WHERE id = p_pack_id;
-- Insert cards into user inventory
INSERT INTO user_cards (user_id, card_id, obtained_from)
SELECT p_user_id, (card->>'id')::uuid, p_pack_id
FROM jsonb_array_elements(v_cards) AS card;
RETURN v_cards;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
For projects that outgrow Supabase’s managed offering or need custom infrastructure, we drop down to raw PostgreSQL on Railway or Fly.io. But that has only happened once in the past two years.
Styling: Tailwind CSS
Tailwind CSS on everything. We stopped debating this years ago. Utility classes colocated with markup, consistent design tokens, tiny production bundles. Every project, every developer, same approach.
We pair Tailwind with a small set of base components we have built up over multiple projects. Nothing fancy — just buttons, inputs, cards, modals — with consistent variants.
// Our typical component pattern in Svelte with Tailwind
// Composition over configuration
<script lang="ts">
import type { HTMLButtonAttributes } from 'svelte/elements';
interface Props extends HTMLButtonAttributes {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
}
let { variant = 'primary', size = 'md', loading = false, children, ...rest }: Props = $props();
const variants = {
primary: 'bg-stone-900 text-white hover:bg-stone-800',
secondary: 'bg-stone-100 text-stone-900 hover:bg-stone-200',
ghost: 'text-stone-600 hover:text-stone-900 hover:bg-stone-50',
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-sm',
lg: 'px-5 py-2.5 text-base',
};
</script>
<button
class="inline-flex items-center justify-center rounded-lg font-medium transition-colors
disabled:opacity-50 {variants[variant]} {sizes[size]}"
disabled={loading}
{...rest}
>
{#if loading}
<span class="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
{/if}
{@render children?.()}
</button>

Hosting and Deployment: Vercel and Cloudflare
We use Vercel for most SvelteKit applications and Cloudflare Pages for Astro sites. The split is practical: Vercel has excellent SvelteKit adapter support and edge middleware we rely on for auth checks. Cloudflare is faster and cheaper for static-heavy sites with edge functions.
Both give us preview deployments on pull requests, which is essential for client reviews. Every PR gets a live URL the client can click. No staging server management, no “it works on my machine” conversations.
For projects that need containers — background workers, cron jobs, custom services — we use Railway. Simple, predictable pricing, good logs, and deploys from a Dockerfile without fuss.
Payments: Stripe
Stripe for everything payment-related. Subscriptions in MindHyv, marketplace payouts in Vincelio, one-time purchases in Just The Rip. We have tried to avoid becoming Stripe experts, but at this point we know the API well enough to handle most payment flows without surprises.
We use Stripe Checkout for most flows because it handles tax calculation, payment method management, and receipt emails without us building any of that. For marketplace projects, Stripe Connect with destination charges.
Email: Resend
Resend replaced SendGrid for us about a year ago. The API is cleaner, the React Email templates are a good fit for our workflow, and deliverability has been solid. We send transactional emails — account verification, invoices, notifications — not marketing campaigns.
Error Tracking: Sentry
Sentry on every production application. Frontend and backend. We have tried cheaper alternatives and always come back. The stack traces, breadcrumbs, and release tracking save us hours when something breaks in production.
Project Management: Linear and GitHub
Linear for task management. We tried Notion, Jira, Asana, and ClickUp over the years. Linear is the only tool that does not slow us down. Fast keyboard shortcuts, good GitHub integration, sensible defaults.
GitHub for code hosting, pull requests, and CI/CD via GitHub Actions. We run linting, type checking, and tests on every PR. Nothing groundbreaking, but the consistency matters.
Design: Figma
Figma for design work. When clients bring their own designs, they are almost always in Figma. When we design in-house, we use Figma. Dev mode for inspecting spacing and extracting values into Tailwind classes works well enough.
Monitoring and Analytics: Plausible and Betterstack
Plausible for web analytics. Privacy-friendly, lightweight, gives us what we need without the complexity of Google Analytics. Clients can see their traffic without needing a Google account.
Betterstack for uptime monitoring and log management. We get a ping when things go down and a dashboard to investigate when they do.

What We Intentionally Do Not Use
A few notable omissions:
- No ORM. We write SQL directly or use Supabase’s query builder. ORMs add a layer of abstraction that, in our experience, creates more debugging than it saves typing.
- No monorepo tooling. At our scale, separate repos with clear boundaries work fine. Turborepo and Nx solve problems we do not have.
- No Kubernetes. Managed platforms handle scaling for every project we have shipped. We are not running infrastructure for the sake of it.
- No CSS-in-JS. Tailwind handles everything. We do not miss styled-components or Emotion.
How We Evaluate New Tools
Our process is simple: one person tries the tool on a side project or internal tool for two weeks. If it is clearly better, we discuss switching. If it is marginally better, we stay with what we know. Switching costs are real, and consistency across the team matters more than chasing the newest thing.
The last tool we added was Resend, replacing SendGrid. The last tool we removed was Prisma, in favor of writing SQL directly. Both decisions took months of real usage before we committed.
The Stack Is Not the Product
We get asked about our stack a lot, and we are happy to share it. But the honest truth is that the stack matters far less than people think. A well-built product on a mediocre stack will outperform a poorly built product on a perfect stack every time.
What matters is that your team knows their tools deeply, that the tools work together without friction, and that you can ship quickly without sacrificing quality. That is what our stack gives us.
If you are evaluating stacks for your next project or curious about whether our approach fits what you are building, reach out at [email protected].