<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ravi</title>
    <description>The latest articles on DEV Community by Ravi (@ravicoding).</description>
    <link>https://dev.to/ravicoding</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2912074%2Fd316b003-c602-44e2-b910-562be0c964f0.png</url>
      <title>DEV Community: Ravi</title>
      <link>https://dev.to/ravicoding</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ravicoding"/>
    <language>en</language>
    <item>
      <title>From a Nobody to 1000 Active Users With $0 in Ads</title>
      <dc:creator>Ravi</dc:creator>
      <pubDate>Sun, 20 Apr 2025 00:58:12 +0000</pubDate>
      <link>https://dev.to/ravicoding/from-a-nobody-to-1000-active-users-with-0-in-ads-1ioa</link>
      <guid>https://dev.to/ravicoding/from-a-nobody-to-1000-active-users-with-0-in-ads-1ioa</guid>
      <description>&lt;p&gt;Started with 0 subs and 0 followers.&lt;br&gt;&lt;br&gt;
Spent $0 on ads.&lt;br&gt;&lt;br&gt;
Now my SaaS has 1K Monthly Active Users (~10K total).&lt;/p&gt;




&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Hi, I'm Ravi. I'm originally from Brazil, but I've been living in Japan for the past 8 years, working full-time as a web developer at a Japanese company. And on the side, I've been building a small bootstrapped startup - plus occasionally posting on my YouTube channel, which somehow ended up with 14,000 subscribers.&lt;/p&gt;

&lt;p&gt;Just a few years ago, I had no idea how any of this worked.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it All Started
&lt;/h3&gt;

&lt;p&gt;Back in March 2022, I was basically a complete beginner in anything related to startups, making money online, or content creation. I didn't know the first thing about investing or entrepreneurship. But I was curious.&lt;/p&gt;

&lt;p&gt;While going down that rabbit hole, I started reading a book called &lt;em&gt;Rich20Something&lt;/em&gt;. I was already turning 30 at the time - so I guess I missed the mark on the title 😅 - but it was the first time I read something that introduced me to the idea of building value instead of just trading time for money.&lt;/p&gt;

&lt;p&gt;I got motivated and tried making my first YouTube video - a programming tutorial. It wasn't great. It took way longer than I expected, and the result was kind of… meh. I told myself I might give it another shot later. For now, I decided to try building something.&lt;/p&gt;

&lt;h3&gt;
  
  
  The First Startup Idea (That Didn't Work Out)
&lt;/h3&gt;

&lt;p&gt;My first real project was called &lt;em&gt;SalonTopia&lt;/em&gt;. The idea was to build a website builder specifically for Japanese salons - many of which didn't have a decent online presence. I imagined this platform that came with online reservations, a CRM to keep track of conversations with clients, maybe even payments and other tools later on.&lt;/p&gt;

&lt;p&gt;The MVP came together pretty nicely, and I planned to charge around $200/month per salon. The math made sense: even getting a few customers could bring in decent revenue.&lt;/p&gt;

&lt;p&gt;But the hard part wasn't building the product. It was getting people to use it.&lt;/p&gt;

&lt;p&gt;I realized I had no connections in that industry. Cold outreach didn't feel right to me. I read books and tried to psych myself up for it, but it just didn't click. And since I had no cofounder to handle sales, I started to consider other options.&lt;/p&gt;

&lt;p&gt;At the same time, something else had caught my attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem Behind the Problem
&lt;/h3&gt;

&lt;p&gt;One small feature in SalonTopia's MVP was a file upload functionality. Getting it to work - with S3, CloudFront, a custom dropzone component - was surprisingly annoying. I remember thinking: "This should be easier."&lt;/p&gt;

&lt;p&gt;That thought kept growing.&lt;/p&gt;

&lt;p&gt;As a developer, I knew I wasn't the only one struggling with this. Uploading files should be simple. And I realized I might be more excited about solving this specific technical problem than about building a salon-focused SaaS.&lt;/p&gt;

&lt;p&gt;That's when I decided to switch gears and start working on what would later become &lt;a href="https://edgestore.dev" rel="noopener noreferrer"&gt;EdgeStore&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Something for Myself
&lt;/h3&gt;

&lt;p&gt;Unlike the salon idea, EdgeStore was something I could deeply relate to. I was the target audience. I knew the pain points. I loved the idea of building tools that help other devs build better things.&lt;/p&gt;

&lt;p&gt;Of course, it came with its own trade-offs. I couldn't charge $200/month per user anymore. But I also didn't need to cold-email anyone. Dev communities are everywhere online, and I figured if I could get the word out, maybe it would catch on.&lt;/p&gt;

&lt;p&gt;So the new plan was: grow my YouTube channel with web dev content, share my progress, get early feedback, polish and build the idea, and eventually make a good video showcasing EdgeStore.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5m7druiilyij509vp2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5m7druiilyij509vp2r.png" alt="Image of YouTube Videos" width="778" height="972"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sudden Competition
&lt;/h3&gt;

&lt;p&gt;At some point, Theo - one of my favorite web dev content creators - had released a product that was very similar to my idea. That made me hesitate for a bit. I didn't want to feel like I was competing with someone I admired.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxg6216r5fuu4i2ip0qn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxg6216r5fuu4i2ip0qn.png" alt="Image of a video from Theo" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But when I shared those thoughts on my channel, the response was incredibly supportive. I ended up deciding to keep going. I didn't want to pivot &lt;em&gt;again&lt;/em&gt;, and I still believed in the value of what I was building.&lt;/p&gt;

&lt;p&gt;Around that time, I was also in the middle of making a YouTube video I thought had real potential.&lt;/p&gt;

&lt;h3&gt;
  
  
  My First Viral Video
&lt;/h3&gt;

&lt;p&gt;It was a video about Tailwind CSS tricks. It took me over a month to make, but the effort paid off.&lt;/p&gt;

&lt;p&gt;It got 3,000 views on the first day - which was more than any other video I had made until this point. Today, it's sitting at over 300,000 views and brought my channel to over 14k subscribers. It also got me monetized on YouTube.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzw5x4tm0bgr0vwifhxlm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzw5x4tm0bgr0vwifhxlm.png" alt="Image of the Tailwind Tricks Video" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I learned so much about content creation and editing from that video.&lt;/p&gt;

&lt;h3&gt;
  
  
  Launching EdgeStore
&lt;/h3&gt;

&lt;p&gt;Using what I learned from the Tailwind video, I made a launch video for EdgeStore. It also got quite a lot of traction. Some well-known dev YouTubers like "Josh Tried Coding" and "Code With Antonio" noticed it and reached out. Antonio even used it in his Notion Clone tutorial, which drove a lot of traffic.&lt;/p&gt;

&lt;p&gt;That launch video (along with the one from Code With Antonio), ended up being the only marketing I really did.&lt;/p&gt;

&lt;p&gt;And they worked.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Results So Far
&lt;/h3&gt;

&lt;p&gt;After the EdgeStore video went live, user signups started climbing. One month in, it had 1,400 users. Today, it's passed 10,000 users - with about 1,000 monthly active and 60 of them as paying customers. Monthly recurring revenue is currently around ¥63K, or about $450 USD.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6m34s8m2ffm79ic8094.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6m34s8m2ffm79ic8094.png" alt="A chart of EdgeStore users in the first month" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The service is stable, the documentation is clear, and I don't get many support questions. So even during the periods when I take a break from it, the numbers keep slowly growing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resting Without Guilt
&lt;/h3&gt;

&lt;p&gt;People often assume that building a startup means working 24/7 and having zero free time. But it doesn't have to be like that.&lt;/p&gt;

&lt;p&gt;I work in waves - a few intense months followed by some quiet ones. I still answer questions and fix bugs quickly, but I don't force myself to always be "on."&lt;/p&gt;

&lt;p&gt;And honestly, that balance is one of the things I'm proud of. This journey is meant to be sustainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Books That Helped Me
&lt;/h3&gt;

&lt;p&gt;I read (or listened to) a lot of books at the beginning of this journey. There are many different ways to build a startup, and I think the right books can vary depending on your personality and goals. If you feel like building things the way I have, here are a few books I enjoyed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show Your Work&lt;/li&gt;
&lt;li&gt;OverSubscribed&lt;/li&gt;
&lt;li&gt;Rich20Something&lt;/li&gt;
&lt;li&gt;Start Small Stay Small&lt;/li&gt;
&lt;li&gt;Company of One&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;I've mostly been focused on improving the service, but lately I've started shifting my attention back to content creation. I'm planning to share more of what I'm building on Twitter (X), and hopefully start making more videos for developers again (including some about EdgeStore).&lt;/p&gt;

&lt;p&gt;For the future, I'm thinking about two possible options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sell the startup to someone who wants to make it grow.&lt;/li&gt;
&lt;li&gt;Try to grow it myself - maybe raise some funding and build a small team.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I haven't made up my mind yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;I'm not an expert. I'm still figuring things out as I go. But if there's one piece of advice I can give, it's this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DON'T OVERTHINK IT. Just start.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Don't wait for the perfect idea. You can always switch them later. You'll learn a lot of things that you can reuse in your next project if the first one doesn't work out.&lt;/p&gt;

&lt;p&gt;So if you've been thinking about building something of your own… maybe today is the time.&lt;/p&gt;

&lt;p&gt;The important thing is to keep making progress with consistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Want to Connect?
&lt;/h3&gt;

&lt;p&gt;Here are my links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://x.com/RaviCoding" rel="noopener noreferrer"&gt;Twitter (X)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/@perfectbase" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://edgestore.dev" rel="noopener noreferrer"&gt;EdgeStore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>saas</category>
      <category>startup</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why I Migrated from Server Actions to tRPC</title>
      <dc:creator>Ravi</dc:creator>
      <pubDate>Fri, 18 Apr 2025 08:29:57 +0000</pubDate>
      <link>https://dev.to/ravicoding/why-i-migrated-from-server-actions-to-trpc-de2</link>
      <guid>https://dev.to/ravicoding/why-i-migrated-from-server-actions-to-trpc-de2</guid>
      <description>&lt;h2&gt;
  
  
  Trying Next.js Without tRPC
&lt;/h2&gt;

&lt;p&gt;I recently tried to build a new Next.js app without &lt;a href="https://trpc.io/" rel="noopener noreferrer"&gt;tRPC&lt;/a&gt;, thinking I wouldn't need it anymore now that Server Actions are stable and there are tools like &lt;a href="https://next-safe-action.dev/" rel="noopener noreferrer"&gt;next-safe-action&lt;/a&gt; to help with validation, auth middleware, error handling, etc.&lt;/p&gt;

&lt;p&gt;Well... I was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  When It's Okay to Skip tRPC
&lt;/h2&gt;

&lt;p&gt;I think I could probably get away without tRPC for apps where the pages are mostly static after the initial page load. In that case, I can just read the data directly from the server component and use server actions with &lt;code&gt;next-safe-action&lt;/code&gt; for the mutations and be happy.&lt;/p&gt;

&lt;h2&gt;
  
  
  When tRPC Becomes Essential
&lt;/h2&gt;

&lt;p&gt;For apps where I have pagination, filtering, sorting, or anything that needs to fetch data after the initial page load, I can't see myself not using tRPC.&lt;/p&gt;

&lt;p&gt;tRPC (together with &lt;a href="https://tanstack.com/query/latest" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt;) makes it super easy to fetch the data from the server component and then be able to easily refetch it when the user interacts with the page. This not only makes the initial page load fast (as there's no initial HTTP request from the browser to fetch the data), but it also makes it super simple to implement things like pagination, filtering, sorting, etc.&lt;/p&gt;

&lt;p&gt;Here's a quick example of using prefetch with useSuspenseQuery for a smooth initial load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HydrateClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trpc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/trpc/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-error-boundary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ClientGreeting&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./client-greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Prefetch the query on the server without blocking the page&lt;/span&gt;
  &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryOptions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HydrateClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Something went wrong&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientGreeting&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HydrateClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// app/client-greeting.tsx&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;trpc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/trpc/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ClientGreeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;trpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useSuspenseQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the benefits of fast server-side rendering and you can use TanStack Query for easily refetching when the user interacts with the page.&lt;/p&gt;

&lt;p&gt;This can be simplified even more with the &lt;code&gt;&amp;lt;Await&amp;gt;&lt;/code&gt; component I shared on Twitter (X) that people seemed to really enjoy:&lt;br&gt;
&lt;a href="https://x.com/RaviCoding/status/1912643165275975711" rel="noopener noreferrer"&gt;https://x.com/RaviCoding/status/1912643165275975711&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Server Actions + TanStack Query?
&lt;/h2&gt;

&lt;p&gt;For those thinking you can just use Server Actions together with useSuspenseQuery from TanStack Query, no, not really. I mean, yeah, it technically works, but if you open the console you'll see an error saying it switched to client-side rendering because Server Actions aren't meant for the initial page load. So a request will still be made from the browser to fetch the initial data.&lt;/p&gt;

&lt;p&gt;Another reason not to use Server Actions for fetching data is that they don't run in parallel. If you're fetching multiple things on the same page from different Server Actions, it'll slow down your page load a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You're Already Using tRPC for Queries...
&lt;/h2&gt;

&lt;p&gt;Now, if I'm already using tRPC to fetch data, there's not much reason not to use it for mutations too, which basically means I end up not using Server Actions at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do You Think?
&lt;/h2&gt;

&lt;p&gt;I'm really curious what others think. Have you found a good way of using Server Actions for these use-cases? Let me know!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>trpc</category>
    </item>
    <item>
      <title>10 Cursor Tips you NEED to Know</title>
      <dc:creator>Ravi</dc:creator>
      <pubDate>Mon, 17 Mar 2025 08:29:26 +0000</pubDate>
      <link>https://dev.to/ravicoding/10-cursor-tips-you-need-to-know-2g70</link>
      <guid>https://dev.to/ravicoding/10-cursor-tips-you-need-to-know-2g70</guid>
      <description>&lt;p&gt;In this article, I'll be sharing 10 tips I wish I knew when I started using Cursor. From basic to advanced, these tips should help you get the most out of Cursor.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Configure VSCode keybindings
&lt;/h2&gt;

&lt;p&gt;You might have noticed that even if you used the VSCode preset when setting up Cursor, the keybindings are not exactly the same. &lt;code&gt;⌘K&lt;/code&gt; related keybindings are remapped to &lt;code&gt;⌘R&lt;/code&gt; and are not easily configurable, so if you're used to using keybindings like &lt;code&gt;⌘+K U&lt;/code&gt; to close unsaved files or &lt;code&gt;⌘+shift+K&lt;/code&gt; to delete a line, you might want to change them.&lt;/p&gt;

&lt;p&gt;To be able to change &lt;code&gt;⌘R&lt;/code&gt; related keybindings back to &lt;code&gt;⌘K&lt;/code&gt;, you will first need to change the &lt;code&gt;workbench.action.keychord.leader&lt;/code&gt; to &lt;code&gt;⌘K&lt;/code&gt;. After that, you'll be able to set the keybindings to the original ones.&lt;/p&gt;

&lt;p&gt;Another option is to use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=YuTengjing.vscode-classic-experience" rel="noopener noreferrer"&gt;VSCode Keybindings for Cursor&lt;/a&gt; extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Know each type of AI
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;autocompletion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When you start typing, Cursor will suggest completions that you can accept by pressing &lt;code&gt;tab&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inline chat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;⌘K&lt;/code&gt; opens the inline chat. Useful when you only want to edit the current file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sidebar chat(Ask)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;⌘L&lt;/code&gt; opens the sidebar chat. You can use it for asking questions about specific files or about the whole project.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sidebar chat(Edit)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Once in the sidebar chat, you can switch to the "Edit" mode. You can use it for editing multiple files at once.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sidebar chat(Agent)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;⌘I&lt;/code&gt; opens the sidebar chat in agent mode. In this mode, the AI can perform more extensive edits, executing them in multiple steps. It can also detect and fix errors in the IDE, as well as execute terminal commands. This is the most powerful way to use Cursor's AI capabilities.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terminal chat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;if the focus is on the terminal, &lt;code&gt;⌘K&lt;/code&gt; opens the terminal chat. You tell the AI what you want to do and it will write the commands for you.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Use docs from tools and frameworks
&lt;/h2&gt;

&lt;p&gt;Cursor has a set of docs for many popular tools and frameworks ready to be used by the AI. You can easily add them to the context by typing &lt;code&gt;@Docs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want a doc that is not in the list, You can add a new doc with the &lt;code&gt;@Docs&lt;/code&gt; command or you can just type &lt;code&gt;@Web&lt;/code&gt; which will make the AI search the web before answering.&lt;/p&gt;

&lt;p&gt;Many docs have been adding an &lt;a href="https://llmstxt.org/" rel="noopener noreferrer"&gt;/llms.txt&lt;/a&gt; page to their website with all the context the AI needs. Here is an example for the &lt;a href="https://turbo.build/llms.txt" rel="noopener noreferrer"&gt;turbo.build&lt;/a&gt; docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Reference files or folders
&lt;/h2&gt;

&lt;p&gt;By using &lt;code&gt;@Files&lt;/code&gt; you can add a specific file to the AI context.&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;@Folders&lt;/code&gt; you can add a whole folder allowing the AI to analyze its contents for answering the prompt.&lt;/p&gt;

&lt;p&gt;You can also start typing the file name or folder name directly after &lt;code&gt;@&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can add all the open files to the context by using the &lt;code&gt;/&lt;/code&gt; command inside the chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Attach images or other files
&lt;/h2&gt;

&lt;p&gt;You can attach design images from Figma or screenshots from other websites that you want Cursor to use as reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Ignore Files
&lt;/h2&gt;

&lt;p&gt;Cursor automatically ignores all patterns in the &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If there are some files that you are committing to git, but you don't want the AI to care about them, you should add them to the &lt;code&gt;.cursorignore&lt;/code&gt; file. (e.g., auto generated files, etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Add project rules
&lt;/h2&gt;

&lt;p&gt;You can add project rules by using the &lt;code&gt;⌘+shift+P&lt;/code&gt; &amp;gt; &lt;code&gt;New Cursor Rule&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You should use it to "teach" the AI about how to do things in your project. (e.g., "Add a new page", "Add an API endpoint", etc.).&lt;/p&gt;

&lt;p&gt;These rules can also be useful for when a new member joins the team. They can use these rules to quickly understand project workflows.&lt;/p&gt;

&lt;p&gt;For more information on this topic, I highly recommend reading this article: &lt;a href="https://ghuntley.com/stdlib/" rel="noopener noreferrer"&gt;You are using Cursor AI incorrectly...&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Use MCP tools
&lt;/h2&gt;

&lt;p&gt;You can add MCP tools that gives the agent some special capabilities. For example, by connecting to your development database, the agent will be able to query the database.&lt;/p&gt;

&lt;p&gt;Many MCP tools are available. Check out this list for reference: &lt;a href="https://github.com/punkpeye/awesome-mcp-servers" rel="noopener noreferrer"&gt;https://github.com/punkpeye/awesome-mcp-servers&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Yolo Mode
&lt;/h2&gt;

&lt;p&gt;By default, the AI agent will ask for confirmation before executing MCP tools or terminal commands. However, if you enable "Yolo Mode" in Cursor's settings, it can execute them without confirmation. Please use this mode with caution as there is a risk of incorrect operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Automate the project setup
&lt;/h2&gt;

&lt;p&gt;By having a clear setup guide on the project's README, new members can use the Cursor composer in agent mode to setup the project. The agent can run all the necessary terminal commands to install the dependencies, set up the environment variables, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I've been using Cursor for a few months now, and I've been enjoying it a lot. I hope you learned some new tips you didn't know before.&lt;/p&gt;

&lt;p&gt;If you have any other tips, please share them in the comments.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>mcp</category>
      <category>cursor</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
