Tech Stack 5

Susan Sarandon
Susan SarandonOriginal
2025-01-04 12:27:35440browse

The React ecosystem is a dynamic landscape of constantly evolving technologies. This article delves into a powerful technology stack to build full-stack applications in 2025, allowing you to bring to life your own product (such as a SaaS) or its Minimum Viable Product (MVP).

As an experienced Fullstack web developer, I've spent years perfecting my approach. Annual reassessments of technology stacks are crucial to balance cutting-edge trends with long-term project stability and maintainability.

My last work experience, which ended in November 2024, has provided me with valuable feedback. Although I am satisfied with the initial technology stack, the retrospective offers valuable lessons for future projects.

Let's embark on this journey of discovery and explore the exciting possibilities that await in this concise yet powerful selection.

React as a full-stack framework

Remix.js

Remix.js is a full-stack web framework that prioritizes the developer experience and leverages web fundamentals to create fast, resilient and user-friendly web applications.

Key Features:

  • Loading and Getting Data: Remix simplifies data getting with built-in loaders and actions. Loaders get data before rendering a route, while actions handle form submissions and other side effects.
  • File System Based Routing: Routes are defined as files within your project's file system, making the routing structure intuitive and easy to understand.
  • Server Side Rendering (SSR): Remix excels at SSR, providing excellent SEO and faster initial load times.
  • Data Transmission: Remix can transmit data to the client, improving perceived performance and user experience.
  • Forms Handling: Integrated form handling simplifies common tasks such as validation, submission, and error handling.
  • Testing: Remix encourages testing at various levels, including unit, integration, and end-to-end testing.
  • Flexibility: Remix can be deployed in various environments, including Node.js servers and edge computing platforms such as Cloudflare Workers.

Benefits of Using Remix:

  • Better Performance: SSR and data streaming contribute to faster loading times and a smoother user experience.
  • Better Developer Experience: Remix's focus on the developer experience makes it enjoyable to use, thanks to its intuitive routing, data fetching mechanisms, and built-in features.
  • Flexibility and Scalability: Remix can be deployed in various environments, making it adaptable to different project needs.
  • Strong Community and Ecosystem: Remix has a growing community and supportive ecosystem with various resources and tools available.

At its core, Remix.js offers a modern and efficient approach to web development, empowering developers to build high-quality, high-performance, user-centric applications.

Star

Considering a dedicated landing page? Astro shines for this task!

While Remix.js excels at monolithic applications that serve both static and dynamic content, Astro offers a compelling alternative specifically for creating exceptional landing pages. Here's why Astro could be the perfect choice:

Key Features:

  • Ultra-Fast Performance: Astro prioritizes speed, delivering lightning-fast landing pages that keep visitors engaged.
  • Intuitive File-Based Routing: Similar to Remix, Astro takes advantage of a file-based routing system, making it easy to structure and manage the content of your landing page.
  • Component Based Development: Build reusable components for an optimized development process and a consistent design across your entire landing page.
  • Integration with Multiple Frameworks: Astro integrates seamlessly with popular frameworks like React, Vue and Svelte, allowing you to leverage your existing skills and preferences.
  • Support for Headless Content Management Systems (CMS): Astro plays well with several headless CMS solutions, allowing flexible management of the content of your landing page.

Benefits of Using Astro for Landing Pages

  • Focus on Developer Experience: Astro's clean syntax and file-based structure simplify development, allowing you to focus on creating an impactful landing page.
  • Rapid Prototyping: Astro's speed makes it ideal for rapid prototyping and iteration in your landing page design.
  • SEO Optimization: Astro generates clean and well-structured HTML, contributing to strong Search Engine Optimization (SEO) for your landing page.
  • Reduced Build Times: Astro's incremental builds minimize build times, allowing for faster development cycles.

By leveraging Astro's strengths, you can create a high-performing, developer-friendly landing page that captures leads and fuels the growth of your SaaS, all while saving valuable time to focus on core product development within your Remix/Next app.

Server Components

Imagine you are building a house. Server Components are like construction workers that handle heavy, specialized tasks. Instead of doing everything in-house (the browser), these components work outside, on the server.

What do they do?

  • Get Materials: Retrieve data from a database or API, like fetching bricks to build a wall.
  • Do complex calculations: Perform complicated mathematical or logical operations, such as calculating the area of ​​a room.
  • Protect your home: They handle security tasks, such as checking if someone has permission to enter.

Why are they useful?

  • Your house is built faster: By doing some of the work on the server, your website loads faster for visitors.
  • T*your home is safer:* Sensitive data is handled in a safer place, out of the reach of intruders.
  • You can focus on decoration: Server components handle the heavy lifting, so you can focus on making your website look good and perform well.

Server Functions

Think about your house having an intercom system. Server Functions are like using that intercom to ask a worker outside the house to do something.

How do they work?

  • You (your React component) tell the worker (the server function) what to do, like "please bring more bricks".
  • The worker does the task and gives you the result.

Why are they useful?

  • It's very easy to communicate: You don't have to worry about the technical details of sending and receiving messages.
  • You can do many things: You can ask the server function to do almost anything that a server component can do.

Server Actions

Imagine that you have a list of predefined commands for your intercom. Server Actions are like those commands.

What are they?

  • These are server functions designed to perform specific tasks, such as submitting a form or updating a database.

Why are they useful?

  • They are easy to use: They are already configured to do something specific, so you don't have to write a lot of code.
  • There are many libraries that help you: There are libraries like next-safe-actions and zsa that provide you with predefined server actions for common tasks.

State Management in React

Zustand

Zustand is a lightweight and flexible state management library for React applications. It offers a simple and intuitive API to manage global and local state, making it a great option for projects of all sizes.

Key Features:

  • Minimalist API: Zustand features a concise and easy-to-learn API with minimal boilerplate code.
  • Performance Oriented: Zustand is designed for optimal performance, with efficient status updates and minimal overhead.
  • Flexible: Offers a flexible and modular approach to state management, allowing you to create and manage multiple stores as needed.
  • Easy to Learn: Simple API and clear documentation make Zustand easy to learn and integrate into your React projects.
import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}

Recoil

Recoil.js is a state management library for React applications that provides a more granular and flexible approach to managing shared state compared to traditional methods like the Context API. It offers a unique data flow graph that allows you to create complex state structures and derive new state from existing ones.

Key Concepts:

  • Atoms: They are the fundamental units of state in Recoil. Atoms are independent and can be subscribed to by multiple components. They provide a way to store and share simple values.
  • Selectors: Selectors are pure functions that derive new state from existing atoms or other selectors. They allow you to create complex state structures and perform calculations on the fly.
  • RecoilRoot: This component is the root of your Recoil application. Provides the context for all Recoil atoms and selectors.
  • Subscriptions: Components subscribe to atoms or selectors to receive updates when the state changes. Recoil uses efficient mechanisms to ensure that components are only re-rendered when the data they depend on has actually changed.

Advanced Features:

  • Asynchronous Values: Recoil supports asynchronous values, allowing you to obtain data from APIs and manage loading states.
  • Persistence: You can persist the Recoil state to local storage or other storage mechanisms to restore it on subsequent page loads.
  • Time Travel Debugging: Recoil provides tools for time travel debugging, allowing you to inspect and roll back state changes.
  • Custom Hooks: You can create custom hooks to encapsulate complex state management logic.
import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}

CSS styles in React

Tailwind CSS

Revolutionary for Rapid Development

Although opinions on Tailwind CSS vary within the developer community, I firmly believe that it is currently the most effective solution for rapid product development and long-term CSS maintenance.

Based on my own experience and feedback from many co-workers, Tailwind offers several key advantages:

  • Rapid Prototyping: Tailwind's utilities-first approach allows developers to quickly build and style UI elements without writing custom CSS classes. This significantly speeds up the prototyping and iteration process.
  • Consistent Styling: Tailwind provides a predefined set of utility classes, ensuring consistent styling throughout your project. This eliminates the need to constantly reinvent the wheel and helps maintain a cohesive design system.
  • Better Developer Experience: Tailwind's intuitive syntax and autocompletion features in modern code editors improve the developer experience, making writing and maintaining CSS faster and more enjoyable.
  • CSS File Size Reduction: By taking advantage of predefined utility classes, you can often significantly reduce the overall size of your CSS files, leading to faster page load times and better performance.
  • Strong Community and Ecosystem: Tailwind has a large and active community, providing access to extensive documentation, useful resources, and a large number of community-built plugins and extensions.

In my experience, the initial learning curve of Tailwind is relatively minor. Most developers become proficient within a week, and the long-term benefits in terms of development speed and maintainability far outweigh the initial investment.

I encourage you to try Tailwind. You might be surprised at how much it can simplify your CSS workflow and increase your productivity.

Getting data in React

React/Tanstack Query

For most data collection needs, I prioritize Server Components due to their inherent performance advantages and improved data security. By handling data loading on the server, I can minimize the amount of JavaScript executed in the browser, resulting in faster initial page loads and a better user experience.

However, for more complex scenarios like infinite scrolling, pagination or real-time data updates, I take advantage of the power of React Query. React Query provides a robust and flexible solution to manage data fetching, caching, and updates on the client side.

Example:

  • Server Components: Ideal for getting initial data for a product page, user profile or blog post.
  • React Query: Great for implementing infinite scroll in a feed, managing paginated data in a table, or handling real-time updates for a chat application.

By strategically combining Server Components and React Query, I can achieve an optimal balance between performance, maintainability and developer experience in my React applications.

Database & ORM

Prism

My Preferred Choice for Database Interactions

Prisma ORM is still my preferred choice for interacting with databases in my React projects. Although newer ORMs like Drizzle are gaining traction, Prisma has proven to be a stable and reliable solution with a strong community and extensive documentation.

Prisma Key Features:

  • Type Safety: Prisma generates TypeScript types from your database schema, ensuring type safety throughout your application and reducing the risk of runtime errors.

Example:

import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}

The generated types for user and posts provide clear guidance and prevent unexpected data structures.

  • Declarative Schema Definition: Define your database schema using Prisma Schema Language, a declarative and intuitive syntax.

Example:

import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}
  • Simplified Query: Prisma provides a fluid and intuitive query creation API, making it easy to write complex database queries with minimal effort.
  • Migrations: Prisma Migrate simplifies database schema changes with an easy-to-use migration system, allowing you to safely evolve your database over time.

Benefits of Using Prism

  • Increased Productivity: Prisma significantly improves developer productivity by automating repetitive tasks such as generating SQL queries and managing database schema changes.
  • Improved Code Quality: Type safety, generated types, and a focus on best practices contribute to higher code quality and fewer bugs.
  • Improved Maintainability: Prisma's declarative approach and clear schema definitions make it easier to understand and maintain database interactions over time.
  • Robust Community and Ecosystem: Prisma has a large and active community, providing access to extensive documentation, tutorials and support resources.

Although newer ORMs like Drizzle offer promising features, Prisma's stability, mature ecosystem, and strong focus on the developer experience make it my preferred choice for most projects.

Supabase

Supabase is an open source alternative to Firebase that offers a full suite of backend services, including a real-time PostgreSQL database, authentication, storage, and edge-functions. It provides developers with a fast and efficient way to build full-stack web applications without the hassle of managing infrastructure.

Supabase Key Features:

  • PostgreSQL in Real Time: Supabase leverages PostgreSQL for its robust database capabilities, allowing you to create complex data models and perform powerful queries. Real-time features allow you to build applications with live updates, such as chat applications and dashboards.
  • Authentication: Supabase provides a flexible authentication system that supports various methods such as email/password, social logins, and custom authentication providers. It also offers features like passwordless authentication and multi-factor authentication.
  • Storage: Supabase includes a file storage service that allows you to upload and manage files directly from your application. You can generate public URLs for files and set permissions to control access.
  • Edge Features: These serverless features allow you to run custom code at the edge, closer to your users. This is useful for tasks like data transformation, server-side rendering, and custom authentication logic.
  • GraphQL API: In addition to the REST API, Supabase also offers a GraphQL API, providing a more flexible and expressive way to query your data.

Why Choose Supabase?

  • Rapid Development: Supabase accelerates development by providing pre-built backend services, allowing you to focus on building the frontend of your application.
  • Scalability: Supabase is built on a scalable infrastructure, making it suitable for applications of all sizes.
  • Open Source: Being open source, Supabase offers transparency, flexibility and a strong community.
  • Cost-effective: Supabase offers a generous free tier and flexible pricing plans, making it affordable for small and large projects.

When to Use Supabase

  • Real-Time Applications: Supabase is ideal for applications that require real-time updates, such as chat applications, collaborative tools and dashboards.
  • Rapid Prototyping: Supabase's ease of use makes it an excellent option for quickly building prototypes and MVPs.
  • Full-Stack Web Applications: Supabase can be used as the backend for both simple and complex web applications.

Data Management and Validation

TypeScript

TypeScript is undeniably the industry standard for JavaScript projects. Its static type system, combined with modern features like interfaces and modules, offers a number of benefits, such as greater type safety, better error detection, higher productivity, and a more enjoyable development experience. The industry's adoption of TypeScript is a testament to its value and effectiveness.

zod

A powerful tool for Type-Safe validation

Zod has emerged as a leading choice for validation in React projects, particularly when combined with TypeScript. By leveraging Zod's type-safe approach, you can significantly improve the robustness and maintainability of your applications.

Zod Key Features

  • Type Safe Validation: Zod leverages TypeScript's type system to define and enforce data schemas. This ensures that the data received by your application conforms to the expected structure, preventing unexpected errors and improving data integrity.
  • Declarative Schemas: Zod allows you to define data schemas declaratively using concise and expressive syntax. This makes it easy to create complex validation rules for your data.
  • Error Handling: Zod provides detailed and informative error messages, making it easy to identify and correct validation problems. These error messages can be easily integrated into your user interface to provide useful feedback to users.
  • Extensibility: Zod offers a flexible and extensible API, allowing you to create custom validation rules and integrate them with other parts of your application.

Benefits of Using Zod

  • Improving Code Quality: By enforcing data types and validation rules, Zod helps you write more robust and reliable code with fewer unexpected errors.
  • Improved Developer Experience: Zod's type-safe approach and informative error messages significantly improve the developer experience by making it easier to write, debug, and maintain your code.
  • Improving User Experience: By providing clear and helpful error messages to users, Zod helps improve the overall user experience of your app.
  • Reduced Maintenance Costs: By detecting data validation issues early on, Zod can help reduce long-term maintenance costs associated with your application.

My Approach

Although Zod offers powerful client-side validation capabilities, I prefer to use it primarily for server-side validation, particularly within Server Actions. This approach keeps client-side forms lightweight and avoids the complexity introduced by many third-party forms libraries. By relying on native HTML validation for basic checks, I can maintain a lean and efficient form component architecture.

Tests and tools

Mock Service Worker (MSW)

A tool that has drastically improved my workflow is Mock Service Worker (MSW). If you don't use it yet, let me show you why it deserves your attention.

Mock Service Worker is a powerful JavaScript library for API mocking. Intercept requests at the network level using Service Workers, allowing you to simulate APIs directly in the browser or at the Node.js runtime. This makes it perfect for testing, debugging, and even development without relying on a backend.

Why I love using MSW

For me, MSW solves many problems that other mocking libraries cannot:

  • Realistic Mocking: MSW intercepts requests at the network level, so the simulated behavior is almost indistinguishable from a real server. It's like having a backend emulator in your pocket.
  • Client and Server Testing: Whether you are testing a React application or a Node.js service, MSW works perfectly in both environments.
  • Dependency Reduction: You don't need additional test servers or complex mocking configurations. MSW keeps it clean and simple.
  • Flexibility: You can simulate REST APIs, GraphQL and even WebSocket. If your app can make the request, MSW can simulate it.
  • Better Debugging: With clear logs and debugging tools, you know exactly which requests are being simulated and how.

MSW vs. Traditional Mocking Tools

In my experience, MSW stands out over tools like Axios interceptors or custom mocks:

  • Scalability: With MSW, your mocks live outside the logic of your application, making them reusable and maintainable.
  • Isolation: Unlike interceptors, MSW does not interfere with your application code. This means no messy teardown code after testing.
  • Browser-Like Behavior: Using Service Workers, MSW mimics browser-level behavior, ensuring your tests are as close to real-world conditions as possible.

Why You Should Try MSW

APIs are the backbone of modern applications, and testing them doesn't have to be painful. MSW provides a realistic, flexible and developer-friendly way to simulate APIs without unnecessary complexity.

Whether you're developing, debugging or testing, MSW is a game changer. It's the tool I didn't know I needed, but now I can't do without.

If you are looking to elevate your development process in 2025, try MSW. Your team will thank you and your code will shine.

Playwright

When it comes to modern web testing in 2025, Playwright has become one of my go-to tools. It's not just a testing library; feels like a power tool for front-end developers who want precision, speed and versatility.

Playwright is a Node.js library for browser automation. Created by Microsoft, it allows you to write end-to-end tests for web applications in all major browsers (Chromium, Firefox, WebKit) with a consistent API. It's like having a swiss army knife for browser testing that is elegant, powerful and easy for developers to use.

Why Playwright stands out

From my experience, Playwright excels at:

  • Multi-Browser Support: Unlike Cypress, which only supports Chromium-based browsers by default, Playwright allows you to test in Chromium, Firefox and WebKit. This makes it essential to ensure that your application works in different environments.
  • Parallel Testing: Playwright's built-in parallelization is a game changer. Tests run faster, keeping the CI pipeline smooth and developers productive.
  • Headless and Headed Modes: Whether you're debugging or running tests in CI, Playwright adapts seamlessly.
  • Context Isolation: With Playwright, you can create isolated browser contexts that mimic different users. This is a lifesaver for applications with complex authentication flows or multi-tenant scenarios.
  • API Testing: Playwright doesn't stop at the UI. You can make API calls directly within your test scripts, ensuring that your front-end and back-end work harmoniously.

Let's look at some code

Here's a quick example of how to write a Playwright test in TypeScript. This test checks a login page:

import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}

Testing is no longer optional in 2025. Users expect seamless experiences, and automation is the way to deliver them. Playwright combines power with developer-friendly features, making it a must-have tool.

If you haven't explored it yet, now is the time. Your future self will thank you when your tests run faster, your errors decrease, and your users are happy.

Deployment and accommodation

Cloudflare (Domain & CDN)

Cloudflare remains a cornerstone of modern web development. For me, it's not just a service, it's an integral part of creating fast, secure and scalable applications. Whether you're an independent developer or part of a large team, Cloudflare has tools that will elevate your stack.

What is Cloudflare?

Cloudflare is a complete set of web performance and security tools. It started as a content delivery network (CDN), but today it is much more. With Cloudflare, you can optimize your website's performance, protect it from malicious attacks, and even create serverless applications using its powerful edge computing platform.

Why I trust Cloudflare

Here are the top reasons why Cloudflare is an essential part of my stack:

  • Speed ​​Everywhere: With its global CDN, Cloudflare ensures that your app's static assets are delivered at lightning speed, no matter where your users are. Its cache is a lifesaver for asset-heavy apps or global audiences.
  • Unmatched Security: Cloudflare's Web Application Firewall (WAF) and DDoS protection have saved me countless headaches. It's like having a security team on autopilot.
  • Serverless Computing at the Edge: Using Cloudflare Workers has been a game changer. It allows me to run lightweight functions at the edge, reducing latency and offloading work from traditional servers.
  • Ease of Use: Setting up Cloudflare takes minutes, but the benefits are immense. Its intuitive dashboard and developer-friendly tools make it easy to integrate with any stack.
  • Cost-effective: For the value it provides, Cloudflare's pricing can't be beat. Even its free tier is packed with features that can help you get started without worrying about costs.

Creating modern applications means offering fast, secure and reliable experiences. Cloudflare allows you to achieve all this without overcomplicating your stack. From its unbeatable CDN to its innovative Edge Computing platform, it is a tool I would recommend to any developer who wants to future-proof their applications in 2025.

If you haven't explored Cloudflare yet, now is the perfect time. Your users will notice the difference, and so will you.

Other Utilities:

React Email & Resend

A powerful combination for email delivery

React Email and Resend offer a compelling solution to create and deliver high-quality emails within your React applications.

  • React Email: This library allows you to create visually appealing and responsive email templates directly within your React components. By leveraging familiar React concepts like components, state, and props, you can create dynamic, easy-to-maintain email layouts.

  • Resend: This robust email API provides a reliable and efficient way to send transactional emails, such as welcome emails, password resets, and order confirmations. Resend offers features such as high deliverability, robust analytics, and easy integration with your existing infrastructure.

Key Benefits

  • Better Developer Experience: React Email improves the developer experience by allowing you to create email templates using familiar React patterns, leading to greater productivity and maintainability.
  • Consistent Branding: By using React components for your email templates, you can ensure consistent branding and styling across your entire app, including emails.
  • Better Deliverability: Resend's robust infrastructure and focus on deliverability help ensure your emails reach their intended recipients reliably.
  • Easy Integration: Resend offers a simple API and SDKs for easy integration with your React application.

Example:

import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}
import { atom, selector, useRecoilState, useRecoilValue } from "recoil";

// Atom for user data (fetched asynchronously)
const userDataAtom = atom({
  key: "userData",
  default: null,
  effects_UNSTABLE: [
    ({ setSelf }) => {
      // Fetch user data from API and set it
    },
  ],
});

// Selector to extract the user's name
const userNameSelector = selector({
  key: "userName",
  get: ({ get }) => {
    const userData = get(userDataAtom);
    return userData?.name || "Guest";
  },
});

function UserProfile() {
  const userName = useRecoilValue(userNameSelector);

  return <div>Hello, {userName}!</div>;
}

This example shows how to use React Email to create a simple welcome email template and then use the Resend API to send it.

By combining the power of React Email and Resend, you can streamline your email workflows, improve the quality of your email communications, and improve the overall user experience.

Stripe

Stripe is a robust and widely used payment gateway that offers a complete set of features for accepting online payments.

Potential challenges

  • Complexity: Although powerful, Stripe's extensive feature set can be overwhelming, especially for smaller projects or those with simpler payment requirements.
  • Evolving API: Stripe continually introduces new features and updates its API, which may occasionally require adjustments to your integration.
import create from "zustand";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>Count: {count}</button>;
}

Essentially, this is the tech stack I would choose today for a new full-stack React project. This is only one possible combination, and the best technology stack for your project will ultimately depend on your specific requirements and priorities. I encourage you to explore these technologies and experiment with different combinations to find what works best for you.

I hope this was helpful and/or taught you something new!

Tech Stack 5

@khriztianmoreno?

The above is the detailed content of Tech Stack 5. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn