Frontend Stack for Solo Founders: Choosing Astro, Next.js, React, Tailwind, and shadcn/ui

"Astro describes itself as a framework for content-driven websites and lists islands, server-first rendering, zero client JavaScript by default, and content collections among its features."
The project has four kinds of pages: /blog, /tools/image-resizer, /dashboard/settings, and /pricing. It is not obvious whether they should all live in one Next.js application or be split into an Astro blog and a Next.js dashboard. A blog already runs on Astro, but adding login, payments, and saved history raises the question of whether to migrate. Meanwhile, a Next.js project with only 20 Markdown posts and a few static pages still asks its owner to understand caching, Server and Client Component boundaries, and deployment configuration.
Choosing a frontend stack for a solo business is not a generic contest for the “best” framework. It is a decision about page types, the amount of dynamic data, maintenance cost, repository boundaries, and whether owning copied shadcn/ui source is worthwhile. The focus here is the boundary: when Astro islands fit, when the maintenance cost of the Next.js App Router exceeds its benefit, and when React + Vite is simpler than Next.js.
Backend languages such as Node.js, Python, and Go, deployment choices such as Cloudflare, Vercel, or self-hosting, databases such as PostgreSQL and Supabase, and authentication products remain separate decisions covered later in this series.
Framework selection table
Start with the page type, not the framework that appears most popular. This table compares the page, dynamic-data level, and maintenance cost before suggesting a baseline:
| Page type | Dynamic data | Maintenance | Recommended baseline | Typical examples |
|---|---|---|---|---|
| Content site: blog, docs, marketing | Low, mainly Markdown/YAML | Low | Astro first | Blog, product docs, landing page |
| Tool site: calculator or formatter | Medium, client state | Medium | React + Vite or Astro islands | Image compressor, JSON formatter, Markdown editor |
| SaaS dashboard: auth, data, settings | High, user data and APIs | High | Next.js App Router | Account settings, orders, analytics dashboard |
| Interactive product: complex or real-time state | High, client routing and live data | High | Next.js or React + Vite | Collaboration tool, chat, editor |
| Marketing and pricing pages | Low, mostly static content | Low | Astro or Next.js SSG | /pricing, /features, /about |
One product with several page types
When a product contains both a content site and a SaaS dashboard, the dominant workload matters.
For a content-led product with roughly 80% content and 20% dashboard functionality, Astro can remain the primary app, with the dashboard implemented as React islands or a separate Next.js application.
For an application-led product with roughly 80% dashboard and 20% blog content, Next.js App Router can own the application and statically generate the blog pages.
When content and application work are close to a 50/50 split, an Astro content app and a Next.js dashboard can make the maintenance boundary clearer. They do not have to be separate repositories; a monorepo can preserve the same boundary.
Splitting apps adds deployment and repository management. Its value is not purity, but preventing caching and rendering rules for one surface from leaking into every other page.
Maintenance-cost warning
Next.js caching, Server and Client Component boundaries, and deployment differences across Vercel, Cloudflare, and self-hosted environments all require attention. If only a few pages are dynamic and most of the site is static, React + Vite or Astro may cost less to maintain.
For a deeper architecture comparison, see the Astro vs Next.js comparison.
Content sites: Astro and the Islands architecture
Astro positions itself as a framework for content-driven websites such as blogs, documentation, marketing pages, and content-rich commerce pages. “Server-first” and “zero JavaScript by default” mean that a page is rendered to HTML at build time or on the server and does not automatically ship client JavaScript. Only explicitly interactive components run in the browser.
Astro content collections organize, validate, and query Markdown or structured data. They work well for blog posts, documentation, and case-study inventories. A collection can validate frontmatter during the build and support typed queries by date, tag, or category, reducing the need to maintain indexes by hand.
Islands: static HTML with local interaction
Most of an island-based page remains server-rendered or prebuilt HTML. A small interactive component becomes an “island,” and directives such as client:load or client:visible decide when it is loaded in the browser.
A submit button can be a React component marked client:load while the rest of the page stays static.
A theme switcher may read localStorage and update CSS variables, so it belongs in a small client component.
An image viewer can use client:visible so its JavaScript loads when the component enters the viewport.
This architecture avoids hydrating an entire React page just to support one form or tool. It is a good fit when a content site needs isolated interaction.
When Astro should not carry the whole product
If nearly every route must check user identity, load private dynamic data, share substantial client state, or support complex client routing, Astro is no longer the obvious baseline. React islands can handle local interaction, but a dashboard made almost entirely of authenticated islands is usually better expressed with Next.js App Router or a standalone React app.
The Astro 5 Lighthouse case study covers content collections and islands in a real content-site setup.
Tool sites and interactive products: React + Vite vs Next.js
A tool site often centers on one action, such as compressing an image or formatting JSON. An interactive product may have several routes, shared state, collaboration, or an editor. React + Vite and Next.js solve different versions of that problem.
When React + Vite fits
React + Vite works well for a client-side SPA or standalone tool that does not depend on server rendering.
An image compressor can process files in the browser and offer the result for download.
A JSON formatter can parse and format input locally without a server route.
A Markdown editor can provide editing, preview, and local draft storage in the browser.
The advantages are a fast build, static deployment, and no Next.js caching or Server/Client Component boundary. The tradeoff is that a client-only tool with important search landing pages needs a deliberate SEO strategy.
When Next.js fits
Next.js is useful when a tool or interactive product needs server rendering, several routes, or mixed rendering.
A multi-page tool can have a landing page, tool route, and result route with server-rendered content.
A search-focused tool page can use static generation or server rendering so its explanatory content is indexable.
A mixed product can keep the tool introduction static while rendering private results dynamically.
When to choose React + Vite
Choose React + Vite when interaction happens entirely through browser APIs, localStorage, or Canvas.
Choose it when the tool can be deployed as static assets without a Node.js runtime.
Choose it when the product does not need Next.js routing, caching, or server rendering.
Choose it when a clear client/backend boundary is easier to maintain than Server and Client Components.
If search traffic and server-rendered routes are central to the tool, compare that benefit with the additional Next.js learning and maintenance cost.
The React 19 Actions article explores form submission and asynchronous interactions in more detail.
SaaS dashboards: Next.js Server and Client Components
The Next.js App Router separates server-rendered work from browser interaction with Server Components and Client Components. A 'use client' directive marks the client boundary.
Server Components vs Client Components
Server Components run on the server or during the build and do not add their component logic to the browser JavaScript bundle.
They are suitable for static content and data access such as database queries or API calls.
They cannot use browser APIs such as localStorage and window, client hooks such as useState and useEffect, or browser event handlers such as onClick.
Client Components run in the browser and support interaction, state, and browser APIs.
They fit forms, buttons, state switches, and live updates.
Their files declare 'use client' at the boundary.
Server Components can import Client Components. A Client Component cannot directly import a Server Component, though server-rendered content can be passed to it as renderable content. This boundary determines where data access and interaction execute.
SaaS dashboard use cases
Next.js App Router fits dashboards that combine authentication, dynamic data, and many forms.
An account settings page reads user data and writes preferences or password changes.
An order system lists orders, opens details, and updates statuses.
An analytics dashboard loads protected data on the server and renders interactive charts in the browser.
Server Components can query a data layer or call a backend service without first exposing every operation as a browser request. That is useful when identity and data permissions are central.
Maintenance-cost warning
Static and dynamic rendering, revalidate, Server and Client Component boundaries, and the selected runtime all need to match the deployed version of Next.js. These rules change, so implementation work should use the current documentation rather than old snippets.
When a dashboard has only a handful of dynamic pages and most of the experience is static, React + Vite with a separate backend such as Node.js or Supabase can be simpler and more portable.
The site’s Next.js App Router series covers routing, migration, Middleware, authentication, and dark mode as separate implementation topics.
Styling layer: Tailwind and utility-first CSS
Utility-first CSS composes small classes directly in HTML or JSX rather than naming a custom CSS class for every component. In <div class="bg-blue-500 text-white p-4 rounded-lg">, each class controls one part of the style.
Tailwind is a styling collaboration layer, not a shortcut to good product design.
It reduces the need to invent and coordinate CSS class names.
It keeps many styles near the markup that uses them, reducing drift between a stylesheet and the rendered component.
It also gives humans and coding agents a shared vocabulary for changing layout and appearance.
Tailwind does not create design quality
A consistent interface still needs decisions about color, typography, spacing, and radius.
Tokens should represent product colors and spacing rather than random combinations of default utilities.
Repeated groups of classes for buttons, cards, and rows should become components instead of being copied everywhere.
Without those constraints, utility classes can make markup denser without making the product more coherent.
Tailwind is independent of the application framework
Tailwind works with Astro, Next.js, and React + Vite. The current Tailwind CSS v4 Vite path uses the @tailwindcss/vite plugin and @import "tailwindcss"; in CSS; Astro can use the same Vite plugin. Check the official framework guide before implementation because commands and integration details can change.
Component layer: shadcn/ui source ownership and integration
shadcn/ui is not a traditional package that hides a fixed component implementation. Its CLI adds component source code to the project, where the project owns and modifies it.
The project owns the component source
Upgrades do not automatically rewrite copied components; the project must review upstream changes and update its code.
Keyboard navigation, ARIA behavior, and screen-reader support still need verification in the product’s actual component combinations.
Default colors, radii, and spacing must be aligned with the product’s design system.
Business behavior such as validation and submission remains application code.
Visible and customizable source is the advantage, but upgrades, accessibility, themes, and business states remain the project’s responsibility.
shadcn/ui and Tailwind
The current shadcn/ui setup guides for Astro and Next.js both expect Tailwind CSS to be configured. Tailwind provides the styling language, while shadcn/ui supplies source code for components.
Suitable use cases
shadcn/ui is useful for SaaS dashboards, settings screens, and form-heavy pages.
A dashboard can start from buttons, inputs, selects, dialogs, and tables.
A settings page can reuse form controls and validation presentation.
Registration, login, and checkout flows can use the primitives, while the product still owns validation and state.
It is a weaker fit when a product already has a component library or requires a complete, tightly governed brand system rather than adaptable primitives.
Astro integration
An Astro project using shadcn/ui needs the React integration so React components can run.
It also needs Tailwind configured for the component styles.
The components are best used for local UI such as a form or dialog, not as a reason to turn every content page into a React application.
The official Astro template currently configures both Tailwind CSS and the React integration. Recheck the current setup before running the CLI.
Next.js integration
shadcn/ui provides a Next.js template as well as an initialization path for existing projects.
Place each component on the correct Server or Client Component side according to its interaction. Using shadcn/ui does not require converting an entire page into a Client Component.
The CLI, presets, and registry configuration can change, so implementation should follow the current documentation.
When not to use shadcn/ui
Do not choose it when the product needs a complete design system rather than component primitives.
Avoid it when the team does not want to own component source, upgrades, accessibility, and theme consistency.
Keep an existing library such as Ant Design, Material UI, or an internal system when it already satisfies the product.
A content-led site with little form or dashboard UI may not need it.
The real maintenance cost for a solo founder
Page type and dynamic data are only part of the decision. Framework complexity, component ownership, and the cost of reviewing AI-generated code all affect long-term work.
Next.js maintenance
Rendering and caching rules can produce stale data when a route’s intended behavior and its configuration disagree.
Server and Client Component boundaries require deliberate decisions about where state and data access live.
Deployment behavior differs across Vercel, Cloudflare, and a self-hosted Node.js runtime and should be evaluated separately.
If most pages are static, the learning and review cost of these boundaries can exceed their benefit.
shadcn/ui maintenance
The project must review component upgrades and upstream fixes.
Keyboard navigation, ARIA attributes, and screen-reader behavior need product-level testing.
Colors, radius, density, and spacing must match the product’s design tokens.
Validation, submission, and application state remain business logic.
If that source ownership is unwanted, a packaged component library or a small set of custom primitives may be a better trade.
Reviewing AI-generated frontend code
Coding agents often produce React, Next.js, and shadcn/ui code readily because examples are abundant. That does not remove review work.
An agent may introduce unnecessary layers of Server and Client Components.
It may apply caching behavior that does not match the version and route semantics in the project.
Generated shadcn/ui combinations may ignore the product’s design tokens and interaction states.
AI can shorten implementation time, but a solo founder still pays for architecture, state, accessibility, and visual acceptance.
Several frameworks in one product
An Astro content site and a Next.js dashboard create a clear boundary, but also add dependencies, configuration, and CI/CD work for two apps.
Each app needs its own deployment and potentially subdomain routing such as blog.example.com and app.example.com.
A solo founder may keep both apps in one monorepo. The important point is that a content-heavy product can stay mostly Astro, an application-heavy product can stay mostly Next.js, and a balanced product can use two explicit app boundaries.
Next steps and further reading
Published articles
Blog framework selection compares Hugo, Astro, and Hexo for content sites.
Astro 5 and a Lighthouse 100 migration covers content collections, islands, and performance configuration.
Astro vs Next.js compares architecture, rendering, and suitable scenarios.
React 19 Actions examines forms, asynchronous actions, and related performance work.
Next.js App Router series
Separate articles in the Next.js App Router series cover routing, migration, Middleware, authentication, and dark mode for readers building SaaS dashboards.
Later articles in this series
This is article five in the Solo Founder Tech Stack series. Later articles cover backend choices such as Node.js, Python, Go, Supabase, and custom APIs.
They also compare deployment on Cloudflare, Vercel, self-hosted servers, and containers.
Database coverage includes PostgreSQL, Supabase, PlanetScale, and MongoDB.
Authentication coverage compares managed and self-built approaches.
After classifying pages by content, dynamic data, and maintenance cost, the next step is to make the backend and deployment boundaries support that frontend choice.
Choose a frontend stack by page type
Classify the pages in the product, then select Astro, Next.js, React/Vite, Tailwind, and shadcn/ui based on interaction, server state, and ownership.
⏱️ Estimated time: 40 min
- 1
Step 1: List the pages
List the blog, tools, pricing, settings, history, and admin pages, then label each one as content, local interaction, authenticated app, or marketing conversion. - 2
Step 2: Map the state boundary
Mark whether each page needs authentication, permissions, private data, complex routing, real-time updates, or substantial client state. - 3
Step 3: Choose the framework baseline
Prefer Astro for content-driven pages, React + Vite or an Astro island for a client-side tool, and Next.js for a dynamic application or dashboard. - 4
Step 4: Choose styling and components
Use Tailwind to organize styling constraints; add shadcn/ui only when the product needs maintainable source for forms, dialogs, tables, and similar components. - 5
Step 5: Define upgrade signals
Treat accounts, saved history, batch jobs, paid quotas, team spaces, and complex permissions as signals to move from a lightweight tool to an application framework. - 6
Step 6: Run acceptance checks
Check mobile layouts, empty, error, and loading states, keyboard focus, key analytics events, and client-component boundaries.
FAQ
Should a solo founder use Astro or Next.js for a content site?
Can Astro power a SaaS dashboard?
Is React + Vite a good choice for an indie tool?
Is Next.js too heavy for a blog?
Are Tailwind and shadcn/ui the same thing?
Does shadcn/ui work with Astro?
Is an all-in-one Next.js stack always easiest for a solo founder?
14 min read · Published on: Oct 9, 2026
Solo Founder Tech Stack Guide: Build, Automate, Ship, and Grow
If you landed here from search, the fastest way to build context is to jump to the previous or next post in this same series.
Previous
How to Combine Codex, Claude Code, and Cursor in a Solo Company
Assign Cursor, Claude Code, and Codex across planning, implementation, review, and release validation, with controls for cost, parallel work, and production risk.
Part 4 of 9
Next
Choosing a Backend Stack for a Solo Business: Cloudflare Workers, Supabase, Node.js, and Databases
Map APIs, webhooks, auth, databases, files, and long-running jobs to Cloudflare Workers, Supabase, or Node.js, with current limits and upgrade signals.
Part 6 of 9



Comments
Sign in with GitHub to leave a comment