My Tech Stack: A Deep Dive into the Tools and Frameworks I Use for Productive Development

DD

Darshan Dhakal

2024-01-04

Here's what I use to make modern sleek website efficently:

1. Framework (Next.js and React)

I’ve been working with Next.js React for very long now(almost 2year self learning) so I'm most productive here. I start all my projects with TypeScript.

For data fetching, I use newer React patterns:

Majority: Server Components

Most of the time, I fetch data in a Server Component (like a page or layout). Occasionally, I'll still use an external library for client-fetches (e.g. Tanstack React Query) when working with existing codebases.

export default async function Page() {

  let data = await fetch('https://api.vercel.app/blog')

  let posts = await data.json()

  return (

    <ul>

      {posts.map((post) => (

        <li key={post.id}>{post.title}</li>

      ))}

    </ul>

  )

}
Occasionally: React use + Context

A newer pattern I've started using is forwarding Promises to a Context provider, and then "unwrapping" the Promise with React’s use.

import React, { createContext, useContext } from 'react'



const BlogContext = createContext(null)



export function BlogProvider({ children }) {

  let blogPromise = fetch('https://api.vercel.app/blog').then(res => res.json())



  return (

    <BlogContext.Provider value={{ blogPromise }}>

      {children}

    </BlogContext.Provider>

  )

}



export function useBlogContext() {

  let context = useContext(BlogContext)

  if (!context) {

    throw new Error('useBlogContext must be used within a BlogProvider')

  }

  return context

}

The data can then be used from a hook in any client component.

'use client'



import { use } from 'react'

import { useBlogContext } from 'app/context'



export function BlogPosts() {

  let { blogPromise } = useBlogContext()

  let posts = use(blogPromise)



  return (

    <ul>

      {posts.map(post => (

        <li key={post.id}>{post.title}</li>

      ))}

    </ul>

  )

}

This can work well if you have global data you need to use deep in the component tree (especially if there are lots of client components).

For forms, After React 19, I have been using new features like Server Actions and useActionState. I’ll also use Zod to validate objects inside my actions. I occasionally use react-hook-form with zod(especially before React 19).

2. Styling (Tailwind CSS and shadcn/ui)

Building flexible, accessible components is difficult. You either use (and extend) a component library, or build your own. This is why I now use shadcn/ui.

It provides well-designed and extensible components, built on top of accessible, unstyled primitives. This includes basics like buttons and inputs, but also icons, charts, and even custom themes.

Components are styled with Tailwind CSS—the most AI-friendly CSS library. Why? You can easily colocate your styles with your markup. This makes generating and editing code with AI tools much easier.

Why Tailwind?

Tailwind uses a compiler to generate only the classes used. So while the utility CSS framework contains many possible class names, only the classes used will be included in the single, compiled CSS file.

Assuming you only write Tailwind code, your bundle will never be larger than the total set of used Tailwind classes. It's extremely unlikely you would use them all. This means you have a fixed upper bound on the size of the generated CSS file, which is then minified, compressed, and cached for the best performance.

You don't have to only write Tailwind styles. Tailwind classes are just utilities that adhere to a design system and work alongside normal CSS. You can mix and match Tailwind with CSS Modules, for example.

3. Database (Postgres and Drizzle)

Postgres is my go-to database. Drizzle makes working with Postgres easy, type-safe, and fun. I can view and modify my data using Drizzle Studio and run migrations with Drizzle Kit.

The best part? Drizzle works perfectly with TypeScript.

4. AI (v0)

v0 helps me edit, refactor, and debug code.

It’s especially helpful for more tedious refactors, or restructuring/reformatting code. Since it has up-to-date knowledge of Next.js, React, Drizzle, and other web tools, it give me great code suggestions.

AI tools aren’t perfect yet, but they’re a huge time saver.

5. Coding Patterns