<?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: Craftly</title>
    <description>The latest articles on DEV Community by Craftly (@getcraftly).</description>
    <link>https://dev.to/getcraftly</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%2F3882284%2F785110de-e7b7-4a25-bd47-94b3e2c1a13e.png</url>
      <title>DEV Community: Craftly</title>
      <link>https://dev.to/getcraftly</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/getcraftly"/>
    <language>en</language>
    <item>
      <title>I Set Up Email Marketing for My Next.js Business in 30 Minutes (Loops.so)</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Wed, 22 Apr 2026 13:37:47 +0000</pubDate>
      <link>https://dev.to/getcraftly/i-set-up-email-marketing-for-my-nextjs-business-in-30-minutes-loopsso-eom</link>
      <guid>https://dev.to/getcraftly/i-set-up-email-marketing-for-my-nextjs-business-in-30-minutes-loopsso-eom</guid>
      <description>&lt;p&gt;Every indie hacker who skipped email marketing eventually regrets it. Your X followers, GitHub stars, or Gumroad audience can disappear overnight if the platform changes. Email is the one channel you actually own.&lt;/p&gt;

&lt;p&gt;Last week I wired email capture into my Next.js template business. From zero subscribers to a working welcome sequence in under an hour. Here's exactly how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loops.so&lt;/strong&gt; — email service built for developers, $0 for up to 1,000 contacts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16.2&lt;/strong&gt; — App Router with Route Handlers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; — hosting with env var management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alternatives considered: Kit (ConvertKit), Resend, Mailchimp. Kit is too creator-focused, Resend is transactional-only, Mailchimp is ancient. Loops is the sweet spot for modern indie products.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Sign Up and Get the API Key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;loops.so → Sign up&lt;/li&gt;
&lt;li&gt;Settings → API → Create API Key&lt;/li&gt;
&lt;li&gt;Copy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Loops requires a sending domain for delivery. You can build the integration before domain is verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Store the Key
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`bash&lt;/p&gt;

&lt;h1&gt;
  
  
  .env.local
&lt;/h1&gt;

&lt;p&gt;LOOPS_API_KEY=&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For Vercel: Settings → Environment Variables. Production + Preview + Development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create the Subscribe Route Handler
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`ts&lt;br&gt;
// src/app/api/subscribe/route.ts&lt;br&gt;
import { NextRequest, NextResponse } from "next/server";&lt;/p&gt;

&lt;p&gt;export const runtime = "edge";&lt;/p&gt;

&lt;p&gt;const CREATE = "&lt;a href="https://app.loops.so/api/v1/contacts/create" rel="noopener noreferrer"&gt;https://app.loops.so/api/v1/contacts/create&lt;/a&gt;";&lt;br&gt;
const UPDATE = "&lt;a href="https://app.loops.so/api/v1/contacts/update" rel="noopener noreferrer"&gt;https://app.loops.so/api/v1/contacts/update&lt;/a&gt;";&lt;/p&gt;

&lt;p&gt;export async function POST(req: NextRequest) {&lt;br&gt;
  try {&lt;br&gt;
    const { email, source = "newsletter" } = await req.json();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!email || typeof email !== "string") {
  return NextResponse.json({ error: "Email required" }, { status: 400 });
}

const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
if (!emailRegex.test(email)) {
  return NextResponse.json({ error: "Invalid email" }, { status: 400 });
}

const apiKey = process.env.LOOPS_API_KEY;
if (!apiKey) {
  return NextResponse.json({ error: "Not configured" }, { status: 500 });
}

const res = await fetch(CREATE, {
  method: "POST",
  headers: {
    Authorization: \`Bearer \${apiKey}\`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email, source, subscribed: true, userGroup: "newsletter",
  }),
});

if (!res.ok) {
  const data = await res.json().catch(() =&amp;gt; ({}));

  // Duplicate? Update instead (idempotent)
  if (res.status === 409 || data?.message?.includes("already")) {
    const updateRes = await fetch(UPDATE, {
      method: "PUT",
      headers: {
        Authorization: \`Bearer \${apiKey}\`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email, subscribed: true, source }),
    });

    if (updateRes.ok) {
      return NextResponse.json({ success: true, existing: true });
    }
  }

  return NextResponse.json({ error: "Failed to subscribe" }, { status: 500 });
}

return NextResponse.json({ success: true });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (err) {&lt;br&gt;
    console.error("Subscribe error:", err);&lt;br&gt;
    return NextResponse.json({ error: "Internal error" }, { status: 500 });&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Key design choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runtime = "edge"\&lt;/code&gt; for fast global response&lt;/li&gt;
&lt;li&gt;Idempotent: duplicate signups become updates&lt;/li&gt;
&lt;li&gt;Email regex validation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;source\&lt;/code&gt; param to track origin&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Build the Subscribe Form
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
"use client";&lt;br&gt;
import { useState } from "react";&lt;/p&gt;

&lt;p&gt;export function NewsletterForm() {&lt;br&gt;
  const [email, setEmail] = useState("");&lt;br&gt;
  const [status, setStatus] = useState&amp;lt;"idle" | "loading" | "success" | "error"&amp;gt;("idle");&lt;br&gt;
  const [message, setMessage] = useState("");&lt;/p&gt;

&lt;p&gt;async function handleSubmit(e: React.FormEvent) {&lt;br&gt;
    e.preventDefault();&lt;br&gt;
    if (!email || status === "loading") return;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;setStatus("loading");
try {
  const res = await fetch("/api/subscribe", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, source: "homepage-footer" }),
  });

  const data = await res.json();

  if (!res.ok) {
    setStatus("error");
    setMessage(data.error || "Something went wrong");
    return;
  }

  setStatus("success");
  setMessage(data.existing ? "You're already on the list!" : "Thanks!");
  setEmail("");
} catch {
  setStatus("error");
  setMessage("Network error");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;if (status === "success") return &lt;/p&gt;✓ {message};

&lt;p&gt;return (&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
      
        type="email"&lt;br&gt;
        value={email}&lt;br&gt;
        onChange={(e) =&amp;gt; setEmail(e.target.value)}&lt;br&gt;
        placeholder="&lt;a href="mailto:you@example.com"&gt;you@example.com&lt;/a&gt;"&lt;br&gt;
        required&lt;br&gt;
      /&amp;gt;&lt;br&gt;
      &lt;br&gt;
        {status === "loading" ? "..." : "Subscribe"}&lt;br&gt;
      &lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;

&lt;p&gt;Drop this anywhere in your layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Verify Sending Domain
&lt;/h2&gt;

&lt;p&gt;Loops won't actually send until you verify a domain.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Loops → Settings → Domain → Add your domain&lt;/li&gt;
&lt;li&gt;Loops generates DNS records:

&lt;ul&gt;
&lt;li&gt;3x CNAME (DKIM)&lt;/li&gt;
&lt;li&gt;1x MX (bounce handling)&lt;/li&gt;
&lt;li&gt;1x TXT (SPF)&lt;/li&gt;
&lt;li&gt;1x TXT (DMARC)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add all to your DNS provider&lt;/li&gt;
&lt;li&gt;Wait 1-6 hours for propagation&lt;/li&gt;
&lt;li&gt;Click Verify&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 6: Build the Welcome Sequence
&lt;/h2&gt;

&lt;p&gt;In Loops UI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Loop&lt;/li&gt;
&lt;li&gt;Trigger: "Contact Added"&lt;/li&gt;
&lt;li&gt;Email 1 (immediate): Welcome + gift + CTA&lt;/li&gt;
&lt;li&gt;Email 2 (Day 2): Value + subtle discount&lt;/li&gt;
&lt;li&gt;Email 3 (Day 5): Story + blog link&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Activate. Every new subscriber gets a 3-part welcome automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Track Sources
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;source\&lt;/code&gt; param religiously. After 30 days you'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"homepage-footer" converts 3x better than "blog-modal"&lt;/li&gt;
&lt;li&gt;One specific blog post drives your best-quality signups&lt;/li&gt;
&lt;li&gt;Certain channels bring higher LTV subscribers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gold for optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;#1. Email validation is worth it.&lt;/strong&gt; Users make typos. Loops charges per contact, bad emails compound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2. The 409 case matters.&lt;/strong&gt; Returning users re-subscribing shouldn't see an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3. Welcome sequences should be written before launch.&lt;/strong&gt; Zero delay between first signup and first email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#4. Track sources.&lt;/strong&gt; Free optimization data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers After Week 1
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Subscribers: 1 (test)&lt;/li&gt;
&lt;li&gt;Open rate: N/A (just activated)&lt;/li&gt;
&lt;li&gt;Domain verification: Processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Day 4 of the business. The point isn't traffic yet — it's having the system ready so when traffic shows up, I capture it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship It
&lt;/h2&gt;

&lt;p&gt;If you're building a digital product, set up email before launch. Every day you delay is subscribers you'll never get back.&lt;/p&gt;

&lt;p&gt;Every &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly template&lt;/a&gt; includes the &lt;code&gt;/api/subscribe\&lt;/code&gt; endpoint + NewsletterForm wired up. Swap in your &lt;code&gt;LOOPS_API_KEY\&lt;/code&gt;, deploy, done. Next.js 16.2 + Tailwind v4 + TypeScript, from $19 to $49 or $99 bundle.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/email-marketing-loops-so-nextjs-template-setup" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out our premium templates:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/mrcek" rel="noopener noreferrer"&gt;SaaSify — SaaS Landing Page&lt;/a&gt; ($49)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/olovds" rel="noopener noreferrer"&gt;Developer Portfolio&lt;/a&gt; ($39)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/ljhgpe" rel="noopener noreferrer"&gt;Blog Template&lt;/a&gt; ($29)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/dvsew" rel="noopener noreferrer"&gt;Pricing Page&lt;/a&gt; ($19)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>marketing</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>How to Build a Developer Portfolio That Gets You Hired in 2026</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Wed, 22 Apr 2026 00:13:03 +0000</pubDate>
      <link>https://dev.to/getcraftly/how-to-build-a-developer-portfolio-that-gets-you-hired-in-2026-4oih</link>
      <guid>https://dev.to/getcraftly/how-to-build-a-developer-portfolio-that-gets-you-hired-in-2026-4oih</guid>
      <description>&lt;h2&gt;
  
  
  Why a Portfolio Matters More Than a Resume
&lt;/h2&gt;

&lt;p&gt;In 2026, your GitHub profile and portfolio website do more work than your resume ever could. A resume tells hiring managers what you've done. A portfolio shows them how you think, what you build, and whether you can ship something that looks good and works well.&lt;/p&gt;

&lt;p&gt;The numbers back this up. Developers with a strong portfolio site get more interview callbacks, negotiate higher salaries, and land freelance contracts faster. Recruiters spend an average of 7 seconds on a resume. They spend significantly more time on a well-built portfolio.&lt;/p&gt;

&lt;p&gt;If you're serious about your career — whether you're landing your first job, switching companies, or going freelance — a portfolio isn't optional anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Hiring Managers Actually Look For
&lt;/h2&gt;

&lt;p&gt;Before you write a single line of code, understand what the person on the other side of the table cares about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proof of Execution
&lt;/h3&gt;

&lt;p&gt;Hiring managers don't want potential — they want evidence. They're looking for shipped projects, not half-finished side projects. Show things that are live, deployed, and usable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Quality and Judgment
&lt;/h3&gt;

&lt;p&gt;Your portfolio itself is a code sample. If it's slow, inaccessible, or poorly structured, that's a signal about how you'll write production code. Every technical decision you make is being evaluated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Ability
&lt;/h3&gt;

&lt;p&gt;Can you explain what a project does and why it matters in two sentences? Developers who can communicate clearly are 10x more valuable than those who can't. Your project descriptions are a writing test in disguise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Personality and Taste
&lt;/h3&gt;

&lt;p&gt;A generic template with placeholder text tells hiring managers nothing. Show your aesthetic preferences, your interests, your writing voice. People hire people they can imagine working with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Essential Sections for a Developer Portfolio
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Hero Section
&lt;/h3&gt;

&lt;p&gt;Your hero is prime real estate. Include your name, a one-line description of what you do (not your job title — your value), and a clear call-to-action. "I build fast, accessible web apps" is better than "Full Stack Developer."&lt;/p&gt;

&lt;p&gt;Add a professional photo if you're comfortable with it. Profiles with photos get significantly more engagement.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Projects (The Core)
&lt;/h3&gt;

&lt;p&gt;This is the most important section. For each project, include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project name and live link&lt;/strong&gt; — always link to something deployed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Problem it solves&lt;/strong&gt; — one sentence on the "why"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tech stack&lt;/strong&gt; — let the reader know what you used&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your role&lt;/strong&gt; — especially important for team projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screenshot or video&lt;/strong&gt; — visuals dramatically increase engagement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aim for 3-5 strong projects rather than 10 mediocre ones. Quality over quantity every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Skills
&lt;/h3&gt;

&lt;p&gt;List technologies you can actually use in a production environment. Group them logically — languages, frameworks, tools, cloud. Avoid skill meters (percentages are meaningless) and don't list things you used once in a tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Experience
&lt;/h3&gt;

&lt;p&gt;Keep it concise. Company, role, dates, and 2-3 bullet points on impact. Use numbers where possible: "Reduced API response time by 40%" beats "Improved performance."&lt;/p&gt;

&lt;p&gt;If you're early in your career, highlight open source contributions, freelance work, or notable side projects instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Blog (Optional but Powerful)
&lt;/h3&gt;

&lt;p&gt;A technical blog is a force multiplier for your portfolio. Every article demonstrates depth of knowledge, communication skill, and continuous learning. It also drives organic search traffic to your site.&lt;/p&gt;

&lt;p&gt;You don't need to post often. Two or three well-written articles on topics you know deeply are worth more than twenty shallow posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Contact
&lt;/h3&gt;

&lt;p&gt;Make it easy to reach you. A simple contact form or a prominent email link is enough. Add your GitHub and LinkedIn icons in the footer. Don't make people hunt for a way to get in touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Tips That Separate Good Portfolios from Great Ones
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Your portfolio needs to load fast. Aim for a Lighthouse performance score above 95. Use &lt;code&gt;next/image\&lt;/code&gt; for optimized images, &lt;code&gt;next/font\&lt;/code&gt; for zero layout shift fonts, and static generation for every page.&lt;/p&gt;

&lt;p&gt;A slow portfolio is a red flag. If you can't optimize your own site, why would a company trust you to optimize theirs?&lt;/p&gt;

&lt;h3&gt;
  
  
  SEO
&lt;/h3&gt;

&lt;p&gt;Add proper metadata to every page. Use the Next.js Metadata API to set unique title tags and descriptions:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;\&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://template-shop-145v.vercel.app/blog/developer-portfolio-guide-2026" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out our premium templates:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/mrcek" rel="noopener noreferrer"&gt;SaaSify — SaaS Landing Page&lt;/a&gt; ($49)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/olovds" rel="noopener noreferrer"&gt;Developer Portfolio&lt;/a&gt; ($39)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/ljhgpe" rel="noopener noreferrer"&gt;Blog Template&lt;/a&gt; ($29)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getcraftly.gumroad.com/l/dvsew" rel="noopener noreferrer"&gt;Pricing Page&lt;/a&gt; ($19)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>design</category>
    </item>
    <item>
      <title>Why I'm Giving Away 8 Next.js Templates for Free (And What I Want in Return)</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Tue, 21 Apr 2026 15:31:30 +0000</pubDate>
      <link>https://dev.to/getcraftly/why-im-giving-away-8-nextjs-templates-for-free-and-what-i-want-in-return-1f6e</link>
      <guid>https://dev.to/getcraftly/why-im-giving-away-8-nextjs-templates-for-free-and-what-i-want-in-return-1f6e</guid>
      <description>&lt;p&gt;Most indie product launches start with a price tag and hope. Mine started with a price tag, a catalog of 8 production-ready Next.js templates, and exactly zero sales.&lt;/p&gt;

&lt;p&gt;So I changed the plan. For 4 weeks — through May 19, 2026 — every Craftly template is free.&lt;/p&gt;

&lt;p&gt;Not discounted. Free. Pay-what-you-want on Gumroad with a $0 minimum.&lt;/p&gt;

&lt;p&gt;Here's why, what's inside, and what I actually want in exchange.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem a discount doesn't solve
&lt;/h2&gt;

&lt;p&gt;When I looked at Craftly honestly, the bottleneck wasn't pricing. The Bundle at $99 is the cheapest full Next.js + Tailwind v4 template catalog I know of. Individual templates at $19-49 beat most competitors on quality.&lt;/p&gt;

&lt;p&gt;The bottleneck was &lt;strong&gt;social proof&lt;/strong&gt;. Zero testimonials. Zero "I shipped this with Craftly" tweets. Zero stories.&lt;/p&gt;

&lt;p&gt;A 20% discount doesn't fix that. It tells people &lt;em&gt;buy now for less&lt;/em&gt;. It doesn't tell them &lt;em&gt;this is worth buying&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Free does something a discount can't: it removes the "is it worth the money?" question entirely and replaces it with "can I use this?"&lt;/p&gt;

&lt;p&gt;That's the question I actually want answered.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the bundle
&lt;/h2&gt;

&lt;p&gt;Every Craftly template, free through May 19:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SaaSify&lt;/strong&gt; — SaaS landing page, 8 sections, conversion-ready&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin Dashboard&lt;/strong&gt; — charts, tables, sidebar, dark mode&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Portfolio&lt;/strong&gt; — 7 sections, single data file to customize&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blog&lt;/strong&gt; — tag filtering, newsletter signup, SEO metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing Page&lt;/strong&gt; — toggle, comparison table, testimonials, FAQ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waitlist&lt;/strong&gt; — countdown, email capture, social proof&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free 404&lt;/strong&gt; — space-themed, always free&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All-Access Bundle&lt;/strong&gt; — all of the above in one listing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Same stack across everything: Next.js 16 App Router, Tailwind v4 with &lt;code&gt;@theme inline&lt;/code&gt; (no config file), TypeScript strict, Framer Motion, Lucide icons. Consistent design system. Dark mode everywhere.&lt;/p&gt;

&lt;p&gt;MIT-friendly license — ship with what you build.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm trading for
&lt;/h2&gt;

&lt;p&gt;The only thing I'm asking for in return is &lt;strong&gt;feedback&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Seven days after you download anything from Craftly, Loops sends you a short email. Three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What did you build (or try to build) with it?&lt;/li&gt;
&lt;li&gt;What almost stopped you? Something confusing, broken, missing?&lt;/li&gt;
&lt;li&gt;Would you tell another dev about it? Why or why not?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reply with one-line answers. Brutal honesty welcome. I'd rather fix what's broken than read compliments.&lt;/p&gt;

&lt;p&gt;That's the whole mechanism. No hidden upsell, no bait-and-switch. The templates are yours to keep forever regardless of whether you reply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the math actually works
&lt;/h2&gt;

&lt;p&gt;Free isn't just a marketing tactic here — it's the highest-leverage thing I can do with Craftly's current state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before free launch&lt;/strong&gt;: 0 paying customers, 0 testimonials, ~20 Dev.to views per article, near-zero X reach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predicted after free launch&lt;/strong&gt;: 100-500 downloads, 10-30 testimonials, meaningful external coverage (HN, Reddit, Dev.to), email list in the hundreds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The templates took weeks to build. Giving them away for 4 weeks costs me exactly $0 in marginal revenue (because marginal revenue was already $0).&lt;/p&gt;

&lt;p&gt;What I gain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A library of real-world feedback I can use to make the next version better&lt;/li&gt;
&lt;li&gt;Early users who might become paying customers when prices restore&lt;/li&gt;
&lt;li&gt;Quotable testimonials that break the zero-to-one social proof trap&lt;/li&gt;
&lt;li&gt;Real conversations with developers building real things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cost-benefit is absurdly lopsided.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens on May 19
&lt;/h2&gt;

&lt;p&gt;Prices return. Bundle goes to $99. Individual templates go to $19-49.&lt;/p&gt;

&lt;p&gt;Anything you downloaded during the free window stays yours — commercial use, personal projects, client work. No clawback, no license change.&lt;/p&gt;

&lt;p&gt;After May 19, I plan to release Lite versions of SaaSify, Portfolio, Blog, and Dashboard — smaller free tier — while keeping Pro versions paid. That keeps a permanent free funnel open after the 4-week window closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to participate
&lt;/h2&gt;

&lt;p&gt;Free launch goes live today. Grab anything at &lt;a href="https://getcraftly.dev" rel="noopener noreferrer"&gt;getcraftly.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No signup required to download — just pay $0 on Gumroad and the zip is yours. Email is only captured if you subscribe to the newsletter, which is optional.&lt;/p&gt;

&lt;p&gt;If you want to help more directly, one public reply to this post or an honest review once you've tried a template would mean a lot.&lt;/p&gt;

&lt;p&gt;This is the whole experiment. Four weeks. Free templates. Honest feedback. We'll see what actually happens.&lt;/p&gt;




&lt;p&gt;Craftly ships production Next.js + Tailwind v4 templates. All 8 are free through May 19, 2026 at &lt;a href="https://getcraftly.dev" rel="noopener noreferrer"&gt;getcraftly.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>indiehackers</category>
      <category>productivity</category>
      <category>marketing</category>
    </item>
    <item>
      <title>Tailwind v4 Container Queries: A Practical Guide with Real Examples</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Mon, 20 Apr 2026 08:42:04 +0000</pubDate>
      <link>https://dev.to/getcraftly/tailwind-v4-container-queries-a-practical-guide-with-real-examples-1e96</link>
      <guid>https://dev.to/getcraftly/tailwind-v4-container-queries-a-practical-guide-with-real-examples-1e96</guid>
      <description>&lt;h1&gt;
  
  
  Tailwind v4 Container Queries: A Practical Guide with Real Examples
&lt;/h1&gt;

&lt;p&gt;Container queries landed as first-class citizens in Tailwind CSS v4. The short version: you can now style an element based on the size of its &lt;em&gt;parent container&lt;/em&gt;, not just the viewport. That unlocks truly reusable components that adapt to wherever you drop them.&lt;/p&gt;

&lt;p&gt;This guide shows the exact syntax, real-world patterns we use in every Craftly template, and the three gotchas that aren't in the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem container queries solve
&lt;/h2&gt;

&lt;p&gt;Before container queries, responsive design was anchored to viewport width. A card component on a full-width mobile layout and a card component tucked into a 300px sidebar on desktop would both get the same media-query breakpoints. The sidebar card looked broken.&lt;/p&gt;

&lt;p&gt;Container queries fix this by letting the card respond to &lt;em&gt;its own available width&lt;/em&gt; instead. Move it from full-width to sidebar and it adapts, same component.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic pattern in Tailwind v4
&lt;/h2&gt;

&lt;p&gt;Three steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Mark the container
&lt;/h3&gt;

&lt;p&gt;Put the &lt;code&gt;@container&lt;/code&gt; class on whatever parent should be the query target:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@container"&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;Card&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Use container variants on children
&lt;/h3&gt;

&lt;p&gt;Inside &lt;code&gt;Card&lt;/code&gt;, use &lt;code&gt;@sm:&lt;/code&gt;, &lt;code&gt;@md:&lt;/code&gt;, &lt;code&gt;@lg:&lt;/code&gt; etc. as variant prefixes — they key off the container, not the viewport:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"p-4 @sm:p-6 @lg:p-8"&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="nt"&gt;h3&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-base @md:text-lg @lg:text-xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Card title&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that card responds to its own parent's width. Drop it in a full-width grid cell, it renders at the &lt;code&gt;@lg&lt;/code&gt; size. Drop it in a narrow sidebar, it stays at the base size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Container sizes default
&lt;/h3&gt;

&lt;p&gt;The default breakpoints are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Container width&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@xs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20rem (320px)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@sm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;24rem (384px)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;28rem (448px)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@lg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;32rem (512px)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@xl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;36rem (576px)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@2xl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;42rem (672px)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can customize these in your &lt;code&gt;@theme inline&lt;/code&gt; block if the defaults don't match your layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real example: a reusable product card
&lt;/h2&gt;

&lt;p&gt;Here's the pattern we use in SaaSify and the Portfolio template. Same component, three contexts:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@container"&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="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"rounded-2xl border bg-card p-4 @sm:p-6 @lg:flex @lg:items-center @lg:gap-8"&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="nt"&gt;img&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;aspect-square&lt;/span&gt; &lt;span class="na"&gt;w-full&lt;/span&gt; &lt;span class="na"&gt;rounded-lg&lt;/span&gt; &lt;span class="na"&gt;object-cover&lt;/span&gt;
                     &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;aspect-&lt;/span&gt;&lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;4&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;3&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;
                     &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;aspect-square&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;w-48&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;flex-shrink-0&lt;/span&gt;&lt;span class="err"&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-4 @lg:mt-0"&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="nt"&gt;h3&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-lg font-semibold @md:text-xl @lg:text-2xl"&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;h3&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="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-2 text-sm text-muted-foreground @md:text-base"&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&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;p&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-4 @lg:mt-6"&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="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl font-bold"&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;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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;span&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="nt"&gt;div&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="nt"&gt;div&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="nt"&gt;article&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="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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop this into any grid and it adapts:&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;// Full-width stacking mobile&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"space-y-4"&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;ProductCard&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// 3-column grid on desktop — each cell renders at @md&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-3 gap-6"&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;ProductCard&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Narrow sidebar — stays at base size&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;aside&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-64"&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;ProductCard&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&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="nt"&gt;aside&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No media-query fiddling. No prop threading a &lt;code&gt;size&lt;/code&gt; variant. Just the card, looking right everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 1: The &lt;code&gt;@container&lt;/code&gt; parent needs explicit sizing
&lt;/h2&gt;

&lt;p&gt;This trips everyone. For container queries to work, the &lt;code&gt;@container&lt;/code&gt; parent needs to have a width that the browser can measure. If the parent is inside a flex column with no explicit width, or inside a grid cell with &lt;code&gt;min-content&lt;/code&gt;, container queries won't fire until a parent establishes sizing.&lt;/p&gt;

&lt;p&gt;Usually this "just works" because most layouts have some grid or flex sizing. But when the parent has no size, add &lt;code&gt;w-full&lt;/code&gt; or put it in a container with known width:&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;// Won't work inside a flex parent without sized children&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex"&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@container"&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;Card&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* @sm: never fires */&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;&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="c1"&gt;// Works — explicit width&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex"&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@container w-full"&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;Card&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="nt"&gt;div&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gotcha 2: Container vs viewport variants are distinct
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@md:&lt;/code&gt; variant (container) and the &lt;code&gt;md:&lt;/code&gt; variant (viewport) are different. Don't mix them thinking one falls back to the other.&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;// These are different&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="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-base md:text-lg @md:text-xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* md:text-lg fires at viewport 768px+ */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* @md:text-xl fires at container 28rem+ */&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick one per component, and usually &lt;code&gt;@&lt;/code&gt; (container) wins for reusability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha 3: Named containers for multi-level queries
&lt;/h2&gt;

&lt;p&gt;If a component nests inside another &lt;code&gt;@container&lt;/code&gt;, you might want children to query the &lt;em&gt;outer&lt;/em&gt; container, not the nearest one. Tailwind v4 supports named containers for this:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@container/outer"&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@container/inner"&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="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@lg/outer:text-xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Queries the outer, ignoring the inner */&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;p&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="nt"&gt;div&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most projects never need this — if you do, name your containers after the semantic role (&lt;code&gt;@container/card&lt;/code&gt;, &lt;code&gt;@container/sidebar&lt;/code&gt;) rather than position.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance notes
&lt;/h2&gt;

&lt;p&gt;Container queries are implemented in the browser with &lt;code&gt;container-type: inline-size&lt;/code&gt; (or &lt;code&gt;size&lt;/code&gt; for two-axis queries). The reflow cost is real but small — Chrome's implementation handles thousands of container-query elements without frame drops.&lt;/p&gt;

&lt;p&gt;Tailwind v4 only emits the &lt;code&gt;container-type&lt;/code&gt; on elements you explicitly mark with &lt;code&gt;@container&lt;/code&gt;, so you don't pay the cost globally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser support
&lt;/h2&gt;

&lt;p&gt;As of 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome/Edge 105+ ✅&lt;/li&gt;
&lt;li&gt;Safari 16+ ✅&lt;/li&gt;
&lt;li&gt;Firefox 110+ ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is to say: everyone who updated Chrome in the last two years has them. The &lt;code&gt;@container&lt;/code&gt; feature is firmly in the "can use without guilt" category.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to use container queries
&lt;/h2&gt;

&lt;p&gt;For layout decisions that depend on user viewport (hamburger menu vs full nav), you still want viewport breakpoints. Container queries are for component-level responsiveness, not app-level layout.&lt;/p&gt;

&lt;p&gt;Rule of thumb: if the component is reusable across multiple layout slots, use &lt;code&gt;@container&lt;/code&gt;. If the decision is global (site header, main content column), use &lt;code&gt;md:&lt;/code&gt;/&lt;code&gt;lg:&lt;/code&gt; viewport breakpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating existing components
&lt;/h2&gt;

&lt;p&gt;If you already have responsive components using viewport breakpoints, you don't have to migrate everything. A sensible refactor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the 3-5 components you reuse in multiple layout contexts&lt;/li&gt;
&lt;li&gt;Wrap each in &lt;code&gt;@container&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;sm:&lt;/code&gt; → &lt;code&gt;@sm:&lt;/code&gt;, &lt;code&gt;md:&lt;/code&gt; → &lt;code&gt;@md:&lt;/code&gt; inside those components&lt;/li&gt;
&lt;li&gt;Test by dropping them into different layout slots&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rest of your app can stay viewport-based.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Container queries in Tailwind v4 let one component serve full-width, grid-cell, and sidebar contexts without duplication or prop-drilled size variants. The three gotchas — sized parents, container vs viewport distinction, and named containers — account for 90% of "why isn't this working" moments. Once you internalize them, you'll wonder how you shipped responsive components before.&lt;/p&gt;

&lt;p&gt;Every Craftly template uses &lt;code&gt;@container&lt;/code&gt; for its primary card components. If you want to see real-world patterns, browse the &lt;a href="https://getcraftly.dev" rel="noopener noreferrer"&gt;catalog at getcraftly.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>webdev</category>
      <category>css</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How to Use Next.js Templates with Cursor and Claude Code (2026 Workflow)</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Mon, 20 Apr 2026 00:26:49 +0000</pubDate>
      <link>https://dev.to/getcraftly/how-to-use-nextjs-templates-with-cursor-and-claude-code-2026-workflow-ba5</link>
      <guid>https://dev.to/getcraftly/how-to-use-nextjs-templates-with-cursor-and-claude-code-2026-workflow-ba5</guid>
      <description>&lt;p&gt;If you bought a Next.js template in 2023, you spent a weekend customizing it. If you buy one in 2026, you spend an afternoon — if you use AI coding tools right.&lt;/p&gt;

&lt;p&gt;Cursor and Claude Code both changed the cost of template customization. This guide shows the workflow I use to go from "cloned template" to "shipped product" in 3-4 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Templates aren't finished products. They're starting points. The real work is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replacing all the copy with your product's voice&lt;/li&gt;
&lt;li&gt;Swapping out demo data with your actual data&lt;/li&gt;
&lt;li&gt;Rebranding colors, fonts, tone&lt;/li&gt;
&lt;li&gt;Adding the one or two sections unique to your product&lt;/li&gt;
&lt;li&gt;Wiring up your actual backend / auth / payments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Historically this was a week of semi-mindless work. AI cuts it to hours. Here's how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Clone and Get It Running
&lt;/h2&gt;

&lt;p&gt;Standard setup. Nothing AI-specific yet.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
unzip saasify-template.zip&lt;br&gt;
cd saasify-template&lt;br&gt;
npm install&lt;br&gt;
npm run dev&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Verify the template works before you touch anything. 30 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Point Your AI Tool at the Repo
&lt;/h2&gt;

&lt;p&gt;This is where 2026 differs from 2023.&lt;/p&gt;

&lt;h3&gt;
  
  
  With Cursor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open the folder in Cursor&lt;/li&gt;
&lt;li&gt;The entire repo is now indexed&lt;/li&gt;
&lt;li&gt;Cmd+K for inline edits, Cmd+L for chat with file context&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With Claude Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;cd into the folder in your terminal&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;claude\&lt;/code&gt; to start a session&lt;/li&gt;
&lt;li&gt;Claude has full read/write access to every file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either works. Cursor feels more like an IDE. Claude Code feels more like pair-programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Ask It to Rebrand
&lt;/h2&gt;

&lt;p&gt;The magic trick: you don't manually hunt through files. You describe what you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`&lt;br&gt;
Rebrand this template from "SaaSify" to "Nimbus". My brand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary color: #10B981 (emerald)&lt;/li&gt;
&lt;li&gt;Accent: #F59E0B (amber)&lt;/li&gt;
&lt;li&gt;Font: Inter (keep)&lt;/li&gt;
&lt;li&gt;Tone: playful, for small team founders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find every mention of "SaaSify" and update to "Nimbus" consistently.&lt;br&gt;
Update CSS variables in globals.css.&lt;br&gt;
Update any brand-colored accents throughout components.&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The AI will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edit src/data/site.ts (or equivalent)&lt;/li&gt;
&lt;li&gt;Update globals.css color tokens&lt;/li&gt;
&lt;li&gt;Find stray brand mentions in components&lt;/li&gt;
&lt;li&gt;Update metadata, OG image config, README&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Review the diff. Accept or iterate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Replace Demo Data
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`&lt;br&gt;
Replace the demo feature cards in src/data/site.ts with our 6 actual features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Invoice automation — "Stop chasing payments. Auto-send reminders."&lt;/li&gt;
&lt;li&gt;Client portal — "Self-serve dashboards your clients actually use."&lt;/li&gt;
&lt;li&gt;Recurring billing — "Subscriptions without the enterprise price tag."
...&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Match the existing data structure. Keep the icon style consistent.&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;AI writes it in the exact shape the components expect. No manual formatting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Add Custom Sections
&lt;/h2&gt;

&lt;p&gt;Templates cover the common case. You always need one weird section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`&lt;br&gt;
I need a new section between Features and Pricing called "How it works":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 numbered steps with icons from lucide-react&lt;/li&gt;
&lt;li&gt;Each step has a title and 1-line description&lt;/li&gt;
&lt;li&gt;Match the existing design language (dark mode support, Framer Motion stagger)&lt;/li&gt;
&lt;li&gt;Create src/components/how-it-works.tsx and compose it into src/app/page.tsx
`&lt;code&gt;\&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI generates the component following your template's patterns. 30 seconds of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Wire Up the Real Stuff
&lt;/h2&gt;

&lt;p&gt;Forms, payments, auth — the parts that differ per project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt for email capture:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`&lt;br&gt;
Wire up the newsletter form in src/components/footer.tsx to Loops.so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create src/app/api/subscribe/route.ts that POSTs to Loops API&lt;/li&gt;
&lt;li&gt;Use LOOPS_API_KEY from .env.local&lt;/li&gt;
&lt;li&gt;Handle loading, success, and error states&lt;/li&gt;
&lt;li&gt;Validate email format client-side
`&lt;code&gt;\&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI writes the endpoint and component update together. You add the env var and test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Review, Don't Accept Everything
&lt;/h2&gt;

&lt;p&gt;This is the part nobody emphasizes enough.&lt;/p&gt;

&lt;p&gt;AI is very good at matching patterns. AI is not always right about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security edges&lt;/strong&gt; — check API validation carefully&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt; — alt text, focus rings, ARIA labels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; — sometimes picks the "works but slow" option&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brand tone&lt;/strong&gt; — AI defaults to "generic SaaS copy"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read every diff before accepting. You're the editor, AI is the writer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Realistic Timeline
&lt;/h2&gt;

&lt;p&gt;Template: SaaSify ($49 Craftly)&lt;br&gt;
Goal: Launch "Nimbus" — an invoicing tool&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clone + run&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Point AI at repo&lt;/td&gt;
&lt;td&gt;2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rebrand prompt + review&lt;/td&gt;
&lt;td&gt;20 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace demo data&lt;/td&gt;
&lt;td&gt;25 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom section&lt;/td&gt;
&lt;td&gt;20 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wire email capture (Loops)&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Final review + polish&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~2.5 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Week-long project → afternoon. That's 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Combo That Works Best
&lt;/h2&gt;

&lt;p&gt;From my experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; for planning and multi-file refactors (rebranding, structural changes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; for rapid inline edits while reviewing the visual result in dev server&lt;/li&gt;
&lt;li&gt;Use both on the same project — they complement each other&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Templates I Ship With This Workflow
&lt;/h2&gt;

&lt;p&gt;Every &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly template&lt;/a&gt; is structured for this AI workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single data file (src/data/*.ts) controls all content — AI knows where to edit&lt;/li&gt;
&lt;li&gt;CSS variables in globals.css for instant rebranding&lt;/li&gt;
&lt;li&gt;TypeScript types everywhere — AI generates valid code&lt;/li&gt;
&lt;li&gt;Clean component boundaries — AI can add sections without breaking existing ones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next.js 16.2 + Tailwind v4 + TypeScript. See current pricing at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;getcraftly.gumroad.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;If you're still customizing templates by hand, you're leaving hours on the table. The 2026 workflow is: buy a good template, point AI at it, iterate until it's yours.&lt;/p&gt;

&lt;p&gt;The skill isn't writing code anymore. It's knowing what to ask for, and being able to tell when AI got it right.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/use-nextjs-templates-with-cursor-claude-code" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cursor</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Tailwind CSS v4 Complete Migration Guide</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Mon, 20 Apr 2026 00:25:42 +0000</pubDate>
      <link>https://dev.to/getcraftly/tailwind-css-v4-complete-migration-guide-3lad</link>
      <guid>https://dev.to/getcraftly/tailwind-css-v4-complete-migration-guide-3lad</guid>
      <description>&lt;p&gt;Tailwind CSS v4 rewrote almost everything that made v3 great. The config file is gone. The plugin system changed. Colors moved to OKLCH. If you are migrating a real project, expect surprises.&lt;/p&gt;

&lt;p&gt;Here is the exact process I use to migrate production apps — including all the traps I fell into so you do not have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Upgrade at All?
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Speed.&lt;/strong&gt; The Oxide engine (Rust-based) is roughly 10x faster at scanning classes. HMR updates feel instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less config.&lt;/strong&gt; Design tokens live in your CSS. The &lt;code&gt;tailwind.config.js\&lt;/code&gt; file is gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern CSS.&lt;/strong&gt; Container queries, &lt;code&gt;@starting-style\&lt;/code&gt;, OKLCH colors — all built in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are on a fresh project, skip v3 entirely. If you have a mature v3 codebase, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Update Dependencies
&lt;/h2&gt;

&lt;p&gt;Remove the old packages and install v4:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
npm uninstall tailwindcss postcss autoprefixer&lt;br&gt;
npm install tailwindcss@next @tailwindcss/postcss&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;@tailwindcss/postcss\&lt;/code&gt; package replaces the old &lt;code&gt;tailwindcss\&lt;/code&gt; PostCSS plugin. It also includes autoprefixing, so you can remove &lt;code&gt;autoprefixer\&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Update PostCSS Config
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;\&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/tailwind-css-v4-migration-complete-guide" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>migration</category>
    </item>
    <item>
      <title>Custom Domains for Next.js: The Cloudflare + Vercel Setup That Works in 2026</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Sun, 19 Apr 2026 11:18:58 +0000</pubDate>
      <link>https://dev.to/getcraftly/custom-domains-for-nextjs-the-cloudflare-vercel-setup-that-works-in-2026-2a3i</link>
      <guid>https://dev.to/getcraftly/custom-domains-for-nextjs-the-cloudflare-vercel-setup-that-works-in-2026-2a3i</guid>
      <description>&lt;p&gt;You shipped a Next.js app to Vercel. It's live at &lt;code&gt;your-project-xxx.vercel.app\&lt;/code&gt;. Everything works, but the URL looks like a throwaway demo.&lt;/p&gt;

&lt;p&gt;Getting a real domain is a 30-minute task, but it's easy to make mistakes that cost you SSL certificates, SEO, or deliverability.&lt;/p&gt;

&lt;p&gt;Here's the exact flow I used to move my template business from a vercel.app URL to my own domain. Production-tested, 2026-current.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registrar&lt;/strong&gt;: Cloudflare Registrar (at-cost pricing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt;: Cloudflare DNS (free, fastest propagation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting&lt;/strong&gt;: Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email Routing&lt;/strong&gt;: Cloudflare Email Routing (free receiving)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email Sending&lt;/strong&gt;: Loops.so&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zero monthly cost except the domain itself (~$12/year for .dev).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Buy the Domain on Cloudflare
&lt;/h2&gt;

&lt;p&gt;Use Cloudflare Registrar, not Namecheap or GoDaddy. Prices are wholesale, WHOIS privacy is free, and DNS is managed in the same dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dash.cloudflare.com → Domain Registration → Register Domain&lt;/li&gt;
&lt;li&gt;Search your domain&lt;/li&gt;
&lt;li&gt;2 years with auto-renew ON&lt;/li&gt;
&lt;li&gt;Pay. Domain is yours in ~60 seconds&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Add the Domain to Vercel
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your Vercel project → &lt;strong&gt;Settings → Domains&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Domain&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your domain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uncheck&lt;/strong&gt; "Redirect to www" — apex as canonical is cleaner in 2026&lt;/li&gt;
&lt;li&gt;Save&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vercel shows &lt;strong&gt;Invalid Configuration&lt;/strong&gt;. Expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Get DNS Records
&lt;/h2&gt;

&lt;p&gt;Click &lt;strong&gt;Edit&lt;/strong&gt; in Vercel → &lt;strong&gt;Manual setup&lt;/strong&gt;. Vercel shows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Type:    CNAME&lt;br&gt;
Name:    @&lt;br&gt;
Value:   &amp;lt;project-hash&amp;gt;.vercel-dns-xxx.com&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Vercel is migrating from the old A record (76.76.21.21) to CNAME-based records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add to Cloudflare DNS
&lt;/h2&gt;

&lt;p&gt;Cloudflare → your domain → &lt;strong&gt;DNS → Records → Add record&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Type:    CNAME&lt;br&gt;
Name:    @&lt;br&gt;
Target:  &amp;lt;Vercel-provided value&amp;gt;&lt;br&gt;
Proxy:   DNS only (gray cloud) ← CRITICAL&lt;br&gt;
TTL:     Auto&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why DNS only matters&lt;/strong&gt;: Cloudflare proxy ON intercepts HTTPS, so Vercel can't issue SSL. Keep it gray.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Wait for Verification
&lt;/h2&gt;

&lt;p&gt;Vercel auto-detects propagation in 2-10 minutes. Green check = done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Apex CNAME Gotcha
&lt;/h2&gt;

&lt;p&gt;DNS spec (RFC 1912) doesn't allow CNAMEs on apex. Cloudflare solves this with &lt;strong&gt;CNAME Flattening&lt;/strong&gt; — internally resolves to IP, returns A record. You don't do anything special.&lt;/p&gt;

&lt;p&gt;AWS Route 53 calls this "ALIAS". Google Cloud DNS has similar. If your DNS provider doesn't support this, use A record with Vercel's IP instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  301 Redirects from Old URL
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;\&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/custom-domain-for-nextjs-cloudflare-vercel-2026" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>dns</category>
      <category>deployment</category>
    </item>
    <item>
      <title>How to Build an Admin Dashboard with Next.js in 2026</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Sun, 19 Apr 2026 11:11:26 +0000</pubDate>
      <link>https://dev.to/getcraftly/how-to-build-an-admin-dashboard-with-nextjs-in-2026-395o</link>
      <guid>https://dev.to/getcraftly/how-to-build-an-admin-dashboard-with-nextjs-in-2026-395o</guid>
      <description>&lt;h2&gt;
  
  
  Why Dashboards Matter for SaaS
&lt;/h2&gt;

&lt;p&gt;Every SaaS product eventually needs an admin dashboard. Whether it's monitoring revenue, tracking active users, or managing customer accounts, dashboards are where data becomes actionable. A well-built dashboard is often the difference between a product that feels professional and one that feels like a prototype.&lt;/p&gt;

&lt;p&gt;Yet dashboards are notoriously time-consuming to build from scratch. You need charts that work, tables that sort and filter, a sidebar that collapses on mobile, stat cards that update in real time, and a layout that stays coherent at every screen size. Get any one of these wrong and the whole thing falls apart.&lt;/p&gt;

&lt;p&gt;In 2026, Next.js 16 with Tailwind CSS v4 is the strongest foundation for building admin dashboards. Here's how to approach it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Components of a Modern Dashboard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stat Cards
&lt;/h3&gt;

&lt;p&gt;Stat cards are the first thing users see — a row of KPI tiles showing the numbers that matter most: total revenue, active users, conversion rate, churn. A good stat card includes the current value, a comparison to the previous period, and a trend indicator (up/down arrow with color coding).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
interface StatCardProps {&lt;br&gt;
  label: string;&lt;br&gt;
  value: string;&lt;br&gt;
  change: number;&lt;br&gt;
  changeLabel: string;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;export function StatCard({ label, value, change, changeLabel }: StatCardProps) {&lt;br&gt;
  const isPositive = change &amp;gt;= 0;&lt;br&gt;
  return (&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
      &lt;p&gt;{label}&lt;/p&gt;
&lt;br&gt;
      &lt;p&gt;{value}&lt;/p&gt;
&lt;br&gt;
      &lt;p&gt;&lt;br&gt;
        {isPositive ? "+" : ""}{change}% {changeLabel}&lt;br&gt;
      &lt;/p&gt;
&lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;

&lt;p&gt;Keep stat cards in a 2x2 or 4-column grid on desktop, stacking to a single column on mobile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Charts
&lt;/h3&gt;

&lt;p&gt;Charts are where most dashboard projects get complicated. You need to choose a library, understand its API, and make it work with your data format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing a Charting Library: Recharts vs Chart.js vs Tremor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Recharts&lt;/strong&gt; is the most popular choice for React dashboards. It's built on SVG, composable, and integrates naturally with React's component model. The API is declarative and easy to read.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts";&lt;/p&gt;

&lt;p&gt;export function RevenueChart({ data }: { data: { date: string; revenue: number }[] }) {&lt;br&gt;
  return (&lt;br&gt;
    &lt;br&gt;
      &lt;br&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;br&gt;
        &lt;br&gt;
      &lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chart.js&lt;/strong&gt; (via react-chartjs-2) is battle-tested and has more chart types, but the API is more imperative and less idiomatic in React. Good for teams coming from a non-React background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tremor&lt;/strong&gt; provides pre-built dashboard components including charts, stat cards, and tables. It's the fastest path to a working dashboard, but less flexible for custom designs.&lt;/p&gt;

&lt;p&gt;For most SaaS dashboards, Recharts is the right choice. It's flexible, actively maintained, and the community is large.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Tables
&lt;/h3&gt;

&lt;p&gt;Tables are the most common dashboard component and the easiest to get wrong. A production-grade data table needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sortable columns (click header to sort, click again to reverse)&lt;/li&gt;
&lt;li&gt;Pagination (server-side for large datasets, client-side for small ones)&lt;/li&gt;
&lt;li&gt;Search and filter&lt;/li&gt;
&lt;li&gt;Row selection for bulk actions&lt;/li&gt;
&lt;li&gt;Responsive behavior (horizontal scroll or card view on mobile)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build your table as a generic component that accepts column definitions and row data. This makes it reusable across different sections of the dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sidebar
&lt;/h3&gt;

&lt;p&gt;The sidebar is the backbone of your dashboard navigation. In 2026, the standard pattern is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desktop:&lt;/strong&gt; Fixed sidebar, 240px wide, with icons and labels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tablet:&lt;/strong&gt; Collapsible sidebar, icons-only mode&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile:&lt;/strong&gt; Off-canvas drawer, triggered by a hamburger menu&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
"use client";&lt;/p&gt;

&lt;p&gt;import { useState } from "react";&lt;/p&gt;

&lt;p&gt;export function Sidebar() {&lt;br&gt;
  const [collapsed, setCollapsed] = useState(false);&lt;/p&gt;

&lt;p&gt;return (&lt;br&gt;
    &lt;br&gt;
       setCollapsed(!collapsed)}&amp;gt;&lt;br&gt;
        {collapsed ? "Expand" : "Collapse"}&lt;br&gt;
      &lt;br&gt;
      {/* nav items */}&lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Store the collapsed state in &lt;code&gt;localStorage\&lt;/code&gt; so it persists across page loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Architecture: The Single Data File Pattern
&lt;/h2&gt;

&lt;p&gt;The cleanest approach for dashboard data is a single data file that exports everything the dashboard needs. This keeps your mock data organized and makes it easy to swap in a real API later.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`ts&lt;br&gt;
// data/dashboard.ts&lt;br&gt;
export const stats = [&lt;br&gt;
  { label: "Total Revenue", value: "$48,295", change: 12.5, changeLabel: "vs last month" },&lt;br&gt;
  { label: "Active Users", value: "2,847", change: 8.2, changeLabel: "vs last month" },&lt;br&gt;
  { label: "Conversion Rate", value: "3.4%", change: -0.6, changeLabel: "vs last month" },&lt;br&gt;
  { label: "Churn Rate", value: "1.2%", change: -0.3, changeLabel: "vs last month" },&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;export const revenueData = [&lt;br&gt;
  { date: "Jan", revenue: 32000 },&lt;br&gt;
  { date: "Feb", revenue: 38000 },&lt;br&gt;
  // ...&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;export const recentOrders = [&lt;br&gt;
  { id: "ORD-001", customer: "Alice Johnson", amount: "$299", status: "Paid" },&lt;br&gt;
  // ...&lt;br&gt;
];&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This pattern works for templates, prototypes, and early-stage products. When you're ready to connect a real API, replace the imports with &lt;code&gt;fetch\&lt;/code&gt; calls — the components don't need to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark Mode Implementation
&lt;/h2&gt;

&lt;p&gt;Dashboard users are almost universally power users who prefer dark mode. Implement it with CSS variables so the switch is instant and doesn't cause a flash of wrong theme.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`css&lt;br&gt;
/* globals.css */&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/import"&gt;@import&lt;/a&gt; "tailwindcss";&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/theme"&gt;@theme&lt;/a&gt; inline {&lt;br&gt;
  --color-background: #ffffff;&lt;br&gt;
  --color-foreground: #0f172a;&lt;br&gt;
  --color-card: #f8fafc;&lt;br&gt;
  --color-border: #e2e8f0;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;.dark {&lt;br&gt;
  --color-background: #0f172a;&lt;br&gt;
  --color-foreground: #f8fafc;&lt;br&gt;
  --color-card: #1e293b;&lt;br&gt;
  --color-border: #334155;&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Store the preference in &lt;code&gt;localStorage\&lt;/code&gt; and apply the &lt;code&gt;.dark\&lt;/code&gt; class to &lt;code&gt;&amp;lt;html&amp;gt;\&lt;/code&gt; before the first paint to avoid flicker:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// app/layout.tsx — inline script runs before React hydrates&lt;br&gt;
const themeScript = \&lt;/code&gt;&lt;br&gt;
  const theme = localStorage.getItem("theme") ?? "dark";&lt;br&gt;
  document.documentElement.classList.toggle("dark", theme === "dark");&lt;br&gt;
`;&lt;/p&gt;

&lt;p&gt;export default function RootLayout({ children }: { children: React.ReactNode }) {&lt;br&gt;
  return (&lt;br&gt;
    &lt;br&gt;
      &lt;/p&gt;
&lt;br&gt;
        &lt;br&gt;
      &lt;br&gt;
      {children}&lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;
&lt;h2&gt;
  
  
  Responsive Sidebar Patterns
&lt;/h2&gt;

&lt;p&gt;The sidebar needs fundamentally different behavior at different breakpoints:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large screens (lg+):&lt;/strong&gt; Persistent sidebar. The main content shifts right when the sidebar is open. Use CSS Grid or Flexbox with a fixed-width sidebar column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Medium screens (md):&lt;/strong&gt; Icon-only sidebar. Keep navigation accessible without consuming too much horizontal space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small screens (sm and below):&lt;/strong&gt; Off-canvas drawer. The sidebar slides in from the left over the content. A backdrop overlay closes it when tapped.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
// Use Tailwind's responsive prefixes to handle this declaratively&lt;/p&gt;

&lt;p&gt;{children}&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started Fast
&lt;/h2&gt;

&lt;p&gt;Building a production-quality admin dashboard from scratch takes weeks. You need to get every component right, handle responsive behavior at every breakpoint, wire up dark mode, and make sure charts look good in both themes.&lt;/p&gt;

&lt;p&gt;Craftly's &lt;a href="https://getcraftly.gumroad.com/l/wzwku" rel="noopener noreferrer"&gt;Dashboard template&lt;/a&gt; ($39) ships with all of this pre-built: stat cards, Recharts integration, a sortable data table, responsive sidebar with collapse state, and full dark mode. The single data file pattern means you can customize the content without touching the component code.&lt;/p&gt;

&lt;p&gt;In the meantime, the patterns above give you everything you need to build your own. Start with the layout and sidebar — get those right and the rest of the components slot in cleanly.&lt;/p&gt;

&lt;p&gt;A great admin dashboard isn't about the number of charts or the complexity of the data. It's about making the right numbers immediately visible and actionable. Focus on that, and the technical decisions become clearer.&lt;/p&gt;

&lt;p&gt;Browse &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly's template collection&lt;/a&gt; for production-ready Next.js templates built with these patterns from the ground up.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/build-admin-dashboard-nextjs" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>design</category>
    </item>
    <item>
      <title>Next.js 16 App Router: The Complete Guide for 2026</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Sun, 19 Apr 2026 07:19:55 +0000</pubDate>
      <link>https://dev.to/getcraftly/nextjs-16-app-router-the-complete-guide-for-2026-2hi3</link>
      <guid>https://dev.to/getcraftly/nextjs-16-app-router-the-complete-guide-for-2026-2hi3</guid>
      <description>&lt;h2&gt;
  
  
  What Is the App Router?
&lt;/h2&gt;

&lt;p&gt;The App Router, introduced in Next.js 13 and now the default in Next.js 16, is a file-system based router built on top of React Server Components. It lives in the &lt;code&gt;app/\&lt;/code&gt; directory and replaces the older &lt;code&gt;pages/\&lt;/code&gt; router as the recommended approach for all new projects.&lt;/p&gt;

&lt;p&gt;If you're still building with &lt;code&gt;pages/\&lt;/code&gt;, the App Router isn't just a new feature — it's a fundamentally different mental model. Understanding it properly will make you a significantly more effective Next.js developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Components vs. Client Components
&lt;/h2&gt;

&lt;p&gt;This is the most important concept in the App Router. Every component in the &lt;code&gt;app/\&lt;/code&gt; directory is a &lt;strong&gt;React Server Component&lt;/strong&gt; (RSC) by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server Components
&lt;/h3&gt;

&lt;p&gt;Server components render on the server and send HTML to the browser. They can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch data directly using &lt;code&gt;async/await\&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Access server-only resources (databases, environment variables)&lt;/li&gt;
&lt;li&gt;Import server-only packages without sending them to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// app/blog/page.tsx — runs entirely on the server&lt;br&gt;
export default async function BlogPage() {&lt;br&gt;
  const posts = await db.query("SELECT * FROM posts");&lt;br&gt;
  return &amp;lt;PostList posts={posts} /&amp;gt;;&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Because server components never ship JavaScript to the browser, they reduce your bundle size dramatically. No &lt;code&gt;useEffect\&lt;/code&gt;, no hydration overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client Components
&lt;/h3&gt;

&lt;p&gt;Client components are the ones you're used to from React. They run in the browser and can use hooks, event listeners, and browser APIs. To opt into client rendering, add &lt;code&gt;"use client"\&lt;/code&gt; at the top of the file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
"use client";&lt;/p&gt;

&lt;p&gt;import { useState } from "react";&lt;/p&gt;

&lt;p&gt;export function Counter() {&lt;br&gt;
  const [count, setCount] = useState(0);&lt;br&gt;
  return  setCount(c =&amp;gt; c + 1)}&amp;gt;{count};&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use "use client"
&lt;/h3&gt;

&lt;p&gt;A common mistake is marking too many components as client components. Use &lt;code&gt;"use client"\&lt;/code&gt; only when the component needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React state (&lt;code&gt;useState\&lt;/code&gt;, &lt;code&gt;useReducer\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Effects (&lt;code&gt;useEffect\&lt;/code&gt;, &lt;code&gt;useLayoutEffect\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Browser APIs (&lt;code&gt;window\&lt;/code&gt;, &lt;code&gt;document\&lt;/code&gt;, &lt;code&gt;localStorage\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Event listeners&lt;/li&gt;
&lt;li&gt;Third-party libraries that require a browser environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pass server-fetched data down to client components as props. Keep the boundary as close to the leaf as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The params Promise API (Breaking Change in Next.js 15+)
&lt;/h2&gt;

&lt;p&gt;This is the biggest breaking change you'll encounter when upgrading. In Next.js 15 and 16, &lt;code&gt;params\&lt;/code&gt; and &lt;code&gt;searchParams\&lt;/code&gt; in page components are now &lt;strong&gt;Promises&lt;/strong&gt;, not plain objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before (Next.js 14)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// Old — params is a plain object&lt;br&gt;
export default function PostPage({ params }: { params: { slug: string } }) {&lt;br&gt;
  return &amp;lt;h1&amp;gt;{params.slug}&amp;lt;/h1&amp;gt;;&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  After (Next.js 15+)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// New — params is a Promise&lt;br&gt;
export default async function PostPage({&lt;br&gt;
  params,&lt;br&gt;
}: {&lt;br&gt;
  params: Promise&amp;lt;{ slug: string }&amp;gt;;&lt;br&gt;
}) {&lt;br&gt;
  const { slug } = await params;&lt;br&gt;
  return &amp;lt;h1&amp;gt;{slug}&amp;lt;/h1&amp;gt;;&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This change enables better streaming and parallel data fetching. It affects every dynamic route in your app, so update all page components when migrating.&lt;/p&gt;

&lt;h2&gt;
  
  
  generateStaticParams
&lt;/h2&gt;

&lt;p&gt;For dynamic routes, &lt;code&gt;generateStaticParams\&lt;/code&gt; tells Next.js which paths to pre-render at build time. This is the App Router equivalent of &lt;code&gt;getStaticPaths\&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// app/blog/[slug]/page.tsx&lt;br&gt;
export async function generateStaticParams() {&lt;br&gt;
  const posts = await fetchAllPosts();&lt;br&gt;
  return posts.map((post) =&amp;gt; ({ slug: post.slug }));&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Any path not returned by &lt;code&gt;generateStaticParams\&lt;/code&gt; will be rendered on-demand (or return 404, depending on your &lt;code&gt;dynamicParams\&lt;/code&gt; setting). For blogs and docs, always use &lt;code&gt;generateStaticParams\&lt;/code&gt; — pre-rendered pages are faster and reduce server load.&lt;/p&gt;

&lt;h2&gt;
  
  
  generateMetadata
&lt;/h2&gt;

&lt;p&gt;Dynamic metadata for SEO is handled by &lt;code&gt;generateMetadata\&lt;/code&gt;, an async function that runs on the server:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
export async function generateMetadata({&lt;br&gt;
  params,&lt;br&gt;
}: {&lt;br&gt;
  params: Promise&amp;lt;{ slug: string }&amp;gt;;&lt;br&gt;
}) {&lt;br&gt;
  const { slug } = await params;&lt;br&gt;
  const post = await fetchPost(slug);&lt;/p&gt;

&lt;p&gt;return {&lt;br&gt;
    title: post.title,&lt;br&gt;
    description: post.description,&lt;br&gt;
    openGraph: {&lt;br&gt;
      title: post.title,&lt;br&gt;
      images: [post.ogImage],&lt;br&gt;
    },&lt;br&gt;
  };&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This gives every page unique, accurate metadata without client-side workarounds. Combined with static generation, it produces near-perfect SEO scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  File Conventions: layout, loading, error
&lt;/h2&gt;

&lt;p&gt;The App Router introduces special file names that automatically wrap your pages:&lt;/p&gt;

&lt;h3&gt;
  
  
  layout.tsx
&lt;/h3&gt;

&lt;p&gt;Layouts wrap pages and persist across navigations. The root layout is required and must include &lt;code&gt;&amp;lt;html&amp;gt;\&lt;/code&gt; and &lt;code&gt;&amp;lt;body&amp;gt;\&lt;/code&gt; tags:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// app/layout.tsx&lt;br&gt;
export default function RootLayout({ children }: { children: React.ReactNode }) {&lt;br&gt;
  return (&lt;br&gt;
    &amp;lt;html lang="en"&amp;gt;&lt;br&gt;
      &amp;lt;body&amp;gt;{children}&amp;lt;/body&amp;gt;&lt;br&gt;
    &amp;lt;/html&amp;gt;&lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nested layouts let you share UI across groups of pages without re-rendering on navigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  loading.tsx
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;loading.tsx\&lt;/code&gt; file to show a loading UI while a page or layout is fetching data. Next.js wraps the page in a &lt;code&gt;&amp;lt;Suspense&amp;gt;\&lt;/code&gt; boundary automatically:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
// app/blog/loading.tsx&lt;br&gt;
export default function Loading() {&lt;br&gt;
  return &amp;lt;div className="animate-pulse"&amp;gt;Loading posts...&amp;lt;/div&amp;gt;;&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  error.tsx
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;error.tsx\&lt;/code&gt; file catches runtime errors in that segment and displays a fallback UI. It must be a client component:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`tsx&lt;br&gt;
"use client";&lt;/p&gt;

&lt;p&gt;export default function Error({ reset }: { reset: () =&amp;gt; void }) {&lt;br&gt;
  return (&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
      &lt;p&gt;Something went wrong.&lt;/p&gt;
&lt;br&gt;
      Try again&lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;
&lt;h2&gt;
  
  
  Performance Tips
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Fetch data at the component level.&lt;/strong&gt; Server components can fetch their own data in parallel. No more prop-drilling data through component trees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use &lt;code&gt;React.cache\&lt;/code&gt; to deduplicate requests.&lt;/strong&gt; If multiple components fetch the same resource, wrap the fetch in &lt;code&gt;cache()\&lt;/code&gt; to avoid redundant database calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Prefer static over dynamic rendering.&lt;/strong&gt; Static pages are faster, cheaper, and cached by default. Only use &lt;code&gt;dynamic = "force-dynamic"\&lt;/code&gt; when the page genuinely needs fresh data on every request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Stream with Suspense.&lt;/strong&gt; Wrap slower data-fetching components in &lt;code&gt;&amp;lt;Suspense&amp;gt;\&lt;/code&gt; so fast parts of the page render immediately while slower parts load in the background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Optimize images with &lt;code&gt;next/image\&lt;/code&gt;.&lt;/strong&gt; It handles lazy loading, resizing, and format conversion automatically. Never use plain &lt;code&gt;&amp;lt;img&amp;gt;\&lt;/code&gt; tags in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;The App Router might feel like a lot to take in, but the mental model is consistent: server components by default, client components only when you need interactivity, and file conventions to handle loading, error, and layout states.&lt;/p&gt;

&lt;p&gt;All Craftly Next.js templates are built with these patterns baked in — proper layout nesting, &lt;code&gt;generateStaticParams\&lt;/code&gt; for dynamic routes, &lt;code&gt;generateMetadata\&lt;/code&gt; for SEO, and &lt;code&gt;"use client"\&lt;/code&gt; used sparingly at the leaf level. It's the best way to see these patterns in production-quality code.&lt;/p&gt;

&lt;p&gt;If you want to accelerate your Next.js development, check out &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly's template collection&lt;/a&gt; — every template is built with Next.js 16, TypeScript, and App Router best practices from the start.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/nextjs-16-app-router-guide" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>react</category>
    </item>
    <item>
      <title>I Used Claude Design on Day 2. Here's What Web Designers Should Know.</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Sun, 19 Apr 2026 03:05:08 +0000</pubDate>
      <link>https://dev.to/getcraftly/i-used-claude-design-on-day-2-heres-what-web-designers-should-know-56gn</link>
      <guid>https://dev.to/getcraftly/i-used-claude-design-on-day-2-heres-what-web-designers-should-know-56gn</guid>
      <description>&lt;p&gt;Anthropic launched Claude Design two days ago. I spent day two putting it through a real test — an hour-long session where I asked it to generate design artifacts for a working business with a real codebase and an existing brand.&lt;/p&gt;

&lt;p&gt;This is an honest field report. Not a hype piece, not a doom piece. What worked, what didn't, and what I think it means for web designers going forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Claude Design
&lt;/h2&gt;

&lt;p&gt;Claude Design is a new product from Anthropic Labs, powered by Opus 4.7. You describe what you want, it generates visual output — prototypes, slide decks, one-pagers, design system artifacts. It reads your codebase and existing design files to maintain consistency.&lt;/p&gt;

&lt;p&gt;Available to paid Claude subscribers at claude.ai/design.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test
&lt;/h2&gt;

&lt;p&gt;I'm an indie product builder. I wanted to see how far Claude Design could go when handed inputs typical of a real engagement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A repo with existing code&lt;/li&gt;
&lt;li&gt;A brand logo (a gradient geometric mark)&lt;/li&gt;
&lt;li&gt;A paragraph of brand voice (dark-first palette, OKLCH colors, tight typography)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I asked it to produce production-grade marketing artifacts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Generated
&lt;/h2&gt;

&lt;p&gt;In one conversational session, it produced:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A complete design system CSS file — brand colors (hex + OKLCH), dark/light semantic tokens, gradients, shadows, radii, spacing scale, and typography preset classes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;17 preview HTML cards&lt;/strong&gt; covering every token and component category (colors, type scales, components, utilities).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A React UI kit&lt;/strong&gt; recreating a marketing site — 10+ &lt;code&gt;.jsx\&lt;/code&gt; files with components, inline Lucide icons, and an interactive demo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A set of 1280x720 cover-image HTML files&lt;/strong&gt;, each with a distinct accent gradient but sharing a unified layout system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because I asked for code, each of these landed as text files — HTML, CSS, JSX, Markdown — that I could read and edit. (If I'd asked for images or PNGs, it would have produced those instead.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Good Part: It Really Did Get Easier
&lt;/h2&gt;

&lt;p&gt;Let me be specific about what "easier" means here, because it's the real story.&lt;/p&gt;

&lt;h3&gt;
  
  
  The workflow is conversational, not point-and-click
&lt;/h3&gt;

&lt;p&gt;The biggest shift from Figma or Canva. I didn't drag rectangles or manipulate layers. I wrote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a cover image with a browser mockup of a landing page on the right and title + price on the left."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it generated that. When I wanted changes, I described them in English:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The logo in the output is wrong — here's the correct one."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fixed. No layer inspector, no components panel, no constraint editor. Just description.&lt;/p&gt;

&lt;h3&gt;
  
  
  When you ask for code, you get code
&lt;/h3&gt;

&lt;p&gt;Ask for HTML, CSS, or JSX and Claude Design outputs exactly that — version-controllable text, not a proprietary design file. You can &lt;code&gt;git add\&lt;/code&gt; a session's output directly. (PNG export is also there when you ask for it — but the default for code-oriented prompts is working code.)&lt;/p&gt;

&lt;h3&gt;
  
  
  A beginner can ship something decent with this alone
&lt;/h3&gt;

&lt;p&gt;This is the part I want to be loud about. If you have an idea and zero design background, you can now produce a coherent-looking landing page, a usable color system, and marketing covers in one afternoon. "I'm not a designer" is no longer an acceptable excuse for shipping ugly things.&lt;/p&gt;

&lt;p&gt;For a hobby project, a side hustle MVP, or a quick pitch deck, &lt;strong&gt;Claude Design alone is enough&lt;/strong&gt;. Really.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Part: "Decent" and "Great" Are Different Tiers
&lt;/h2&gt;

&lt;p&gt;Here's the thing people won't tell you in the launch-week hype.&lt;/p&gt;

&lt;p&gt;Claude Design hands you a &lt;strong&gt;solid baseline&lt;/strong&gt; in minutes. But the gap between "solid baseline" and "actually good" still costs knowledge, taste, and iteration — and AI can't skip that for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the baseline falls short
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First-pass outputs drift.&lt;/strong&gt; Three or four iterations were normal — wrong asset pulled from the repo, proportions slightly off, spacing rhythm inconsistent between screens. If you don't &lt;em&gt;see&lt;/em&gt; the drift, you ship it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine control is harder than big direction.&lt;/strong&gt; "Apply a subtle mesh gradient with grain overlay" works instantly. Dialing in exact grain intensity so it reads as texture instead of noise? I dropped into the CSS myself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It has no taste about your taste.&lt;/strong&gt; It matches &lt;em&gt;a&lt;/em&gt; plausible style guide given your inputs. Whether that's the &lt;em&gt;right&lt;/em&gt; style for your audience, your positioning, your category — it can't tell you. Only you can.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems thinking is still yours.&lt;/strong&gt; AI happily generates ten cover variations. Deciding which three ship, what the naming convention is, how this scales to the next twenty covers — that's a systems problem, not a generation problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The knowledge that still pays off
&lt;/h3&gt;

&lt;p&gt;If you actually want output that looks &lt;em&gt;considered&lt;/em&gt; rather than just &lt;em&gt;competent&lt;/em&gt;, you still need to know things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why this spacing scale and not a different one&lt;/li&gt;
&lt;li&gt;When a gradient reads as premium vs. when it reads as dated&lt;/li&gt;
&lt;li&gt;How type contrast actually works (not just "bigger + bolder")&lt;/li&gt;
&lt;li&gt;What a design system's job is beyond "list of colors"&lt;/li&gt;
&lt;li&gt;How to read a layout and diagnose &lt;em&gt;why&lt;/em&gt; it feels off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing about Claude Design changes that. It just makes the generation step cheap, so &lt;strong&gt;the knowledge-shaped bottleneck shows up earlier and more visibly&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Different People
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If you're a beginner or non-designer
&lt;/h3&gt;

&lt;p&gt;Great news. The floor just rose dramatically. You can ship things that don't look embarrassing, without six months of design study first. Use it.&lt;/p&gt;

&lt;p&gt;Just don't confuse "no longer embarrassing" with "great." If your goal is something that resonates beyond "acceptable," you'll still hit a ceiling that only design knowledge removes.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're a working designer
&lt;/h3&gt;

&lt;p&gt;Your execution speed is no longer the thing that makes you valuable. Anyone with a prompt can generate variations now. What compounds is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Judgment&lt;/strong&gt; — which of the ten outputs ships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direction&lt;/strong&gt; — what should be made in the first place&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Craft at the edges&lt;/strong&gt; — the 20% of polish AI won't reach by default&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems&lt;/strong&gt; — turning one-off artifacts into reusable, consistent work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all things AI accelerates your process around, not things it replaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're a design system maintainer
&lt;/h3&gt;

&lt;p&gt;Probably the biggest shift. Claude Design reads your codebase and generates artifacts that match your existing tokens. Grunt work — refreshing preview pages, recreating examples, documenting new components — becomes near-free. The human role concentrates on defining principles and reviewing output for consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What To Actually Do
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Don't panic, don't dismiss.&lt;/strong&gt; "AI will kill design" and "AI is just a toy" are both wrong for the same reason — they skip the part where &lt;em&gt;how&lt;/em&gt; you use it decides the outcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn prompt craft, but don't stop there.&lt;/strong&gt; Describing visual work precisely is a real skill. But a great prompt without design knowledge still produces mediocre design faster. Prompt craft is a multiplier; knowledge is the base number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep studying the fundamentals.&lt;/strong&gt; Type, hierarchy, rhythm, color, systems. The floor went up — so the relative value of being &lt;em&gt;above&lt;/em&gt; the floor went up too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrate it, don't fight it.&lt;/strong&gt; Use AI for first drafts, exploration, system documentation, mundane asset generation. Reserve your focus for decisions and polish that actually need you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Honest Bottom Line
&lt;/h2&gt;

&lt;p&gt;Claude Design makes "decent output" trivially easy. That's a big deal — a beginner can ship something they'd have been embarrassed to ship a year ago.&lt;/p&gt;

&lt;p&gt;But "decent" and "great" are still different tiers, and the gap between them is still paid in knowledge and judgment. AI shrinks the distance from "idea" to "baseline." It doesn't shrink the distance from "baseline" to "considered, intentional, well-crafted work."&lt;/p&gt;

&lt;p&gt;Which, honestly, is the best possible outcome. The boring part got cheap. The interesting part — knowing what's worth making, and making it well — is still where humans live.&lt;/p&gt;




&lt;p&gt;Looking for hand-crafted, production-ready Next.js templates with careful attention to detail? Take a look at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt; — Tailwind v4 native, Next.js 16.2, TypeScript strict. Real code, carefully built.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/claude-design-review-web-designer-future-2026" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>futureofwork</category>
    </item>
    <item>
      <title>Next.js 16.2 Turbopack: 400% Faster next dev — What It Actually Feels Like</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Sat, 18 Apr 2026 12:16:58 +0000</pubDate>
      <link>https://dev.to/getcraftly/nextjs-162-turbopack-400-faster-next-dev-what-it-actually-feels-like-2934</link>
      <guid>https://dev.to/getcraftly/nextjs-162-turbopack-400-faster-next-dev-what-it-actually-feels-like-2934</guid>
      <description>&lt;p&gt;Next.js 16.2 shipped last month with one headline claim: &lt;code&gt;next dev\&lt;/code&gt; is now 400% faster. The official release notes promise 10x HMR on large projects. Turbopack replaces Webpack as the default bundler.&lt;/p&gt;

&lt;p&gt;I've been running it on five production-grade template repos for two weeks. Here's what that actually feels like in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Boring Answer: Yes, It's Fast
&lt;/h2&gt;

&lt;p&gt;Here are the numbers I saw across the Craftly template repos (Next.js 16.2.2, MacBook Air M2):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;15.x &lt;code&gt;next dev\&lt;/code&gt; cold&lt;/th&gt;
&lt;th&gt;16.2 &lt;code&gt;next dev\&lt;/code&gt; cold&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SaaSify (8 sections, ~40 components)&lt;/td&gt;
&lt;td&gt;6.8s&lt;/td&gt;
&lt;td&gt;1.4s&lt;/td&gt;
&lt;td&gt;4.9x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard (sidebar, charts, tables)&lt;/td&gt;
&lt;td&gt;9.2s&lt;/td&gt;
&lt;td&gt;1.9s&lt;/td&gt;
&lt;td&gt;4.8x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blog (tag filter, 6 posts, typography)&lt;/td&gt;
&lt;td&gt;5.1s&lt;/td&gt;
&lt;td&gt;1.3s&lt;/td&gt;
&lt;td&gt;3.9x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 400% claim isn't marketing. If anything, it undersells the subjective experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where You Feel It Most
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Cold start on large projects
&lt;/h3&gt;

&lt;p&gt;Before: you &lt;code&gt;npm run dev\&lt;/code&gt;, go make coffee, come back.&lt;br&gt;
Now: it's ready before your terminal prompt settles.&lt;/p&gt;

&lt;p&gt;On a fresh clone of a monorepo, the difference is the gap between "I'll grab a drink" and "oh, it's already running."&lt;/p&gt;

&lt;h3&gt;
  
  
  2. HMR on deeply nested components
&lt;/h3&gt;

&lt;p&gt;Editing a prop in a component 6 folders deep used to produce a visible lag — 200-500ms before the browser reflected the change. On 16.2, it's effectively instant. You stop noticing HMR as a step.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Type-checking doesn't block you
&lt;/h3&gt;

&lt;p&gt;Turbopack runs type-checking async. If your types are wrong, you still get the error, but your page doesn't hang waiting for the compiler.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Catches (there are always catches)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Webpack plugins may not work
&lt;/h3&gt;

&lt;p&gt;If you had a custom webpack config — analyzers, special loaders, module federation — check whether the Turbopack equivalent exists. Most common ones (bundle-analyzer, mdx) are covered. Exotic setups may need migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;next.config.ts\&lt;/code&gt; assumptions
&lt;/h3&gt;

&lt;p&gt;Config that worked on Webpack may need tweaking. The clearest symptom: missing files in production build that were fine in dev. Always do a full &lt;code&gt;next build\&lt;/code&gt; before deploying.&lt;/p&gt;

&lt;h3&gt;
  
  
  HMR for Server Components got better, but not perfect
&lt;/h3&gt;

&lt;p&gt;16.2 adds "Server Fast Refresh" — hot reload for server components. It works for most edits. Complex changes (moving between RSC and Client, changing layouts) still sometimes require a full page reload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should You Upgrade?
&lt;/h2&gt;

&lt;p&gt;If you're on 15.x → &lt;strong&gt;yes, today&lt;/strong&gt;. The upgrade is incremental, and the DX improvement alone justifies it. Budget 30 minutes for a small app, an afternoon for a larger one.&lt;/p&gt;

&lt;p&gt;If you're on 14.x or earlier → upgrade staged. Go 14 → 15.0 → 15.x latest → 16.2. Don't jump directly.&lt;/p&gt;

&lt;p&gt;If you're on 13.x with &lt;code&gt;pages/\&lt;/code&gt; → different question. You're migrating App Router and Turbopack at once. Budget more than a weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Templates
&lt;/h2&gt;

&lt;p&gt;I ship Next.js templates for a living, and the jump from 15.x to 16.2 changes the baseline.&lt;/p&gt;

&lt;p&gt;A template that ships with 15.x gets outdated the moment a buyer runs &lt;code&gt;npm install\&lt;/code&gt; in three months. A template that ships 16.2 with Turbopack default gives buyers the speed boost without them knowing they wanted it.&lt;/p&gt;

&lt;p&gt;This is also why most templates on Gumroad still show &lt;code&gt;pages/\&lt;/code&gt; in their structure. They were built for 13/14, never updated. Buyers get code that technically works but feels slow the moment they start editing.&lt;/p&gt;

&lt;p&gt;All &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly templates&lt;/a&gt; ship 16.2 with Turbopack default out of the box. No config migration needed — it's already done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;400% faster dev server means you're willing to try more things.&lt;/p&gt;

&lt;p&gt;You don't batch edits because HMR is slow. You don't &lt;code&gt;console.log\&lt;/code&gt; 10 things to avoid a full reload. You just... edit, look, edit, look. The feedback loop tightens, and the quality of your code improves because you iterate more.&lt;/p&gt;

&lt;p&gt;The actual unlock isn't the benchmark number. It's the change in how you work.&lt;/p&gt;




&lt;p&gt;If you're shipping something this weekend, upgrade to 16.2 first. You'll save more than you spend.&lt;/p&gt;

&lt;p&gt;And if you want a Next.js 16.2 + Tailwind v4 + TypeScript template to start from, browse the full catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;getcraftly.gumroad.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/nextjs-16-2-turbopack-honest-review" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>turbopack</category>
      <category>performance</category>
    </item>
    <item>
      <title>SaaS Pricing Page Best Practices: Design That Converts</title>
      <dc:creator>Craftly</dc:creator>
      <pubDate>Sat, 18 Apr 2026 11:27:56 +0000</pubDate>
      <link>https://dev.to/getcraftly/saas-pricing-page-best-practices-design-that-converts-3adn</link>
      <guid>https://dev.to/getcraftly/saas-pricing-page-best-practices-design-that-converts-3adn</guid>
      <description>&lt;h2&gt;
  
  
  Your Pricing Page Is Your Most Important Page
&lt;/h2&gt;

&lt;p&gt;For most SaaS products, the pricing page is the highest-intent page on your site. Visitors who reach it are seriously considering buying. A well-designed pricing page can double your conversion rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three-Tier Rule
&lt;/h2&gt;

&lt;p&gt;Three tiers work best for most SaaS products. Fewer feels limiting, more feels overwhelming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Starter / Free Tier
&lt;/h3&gt;

&lt;p&gt;Your free tier serves two purposes: it lowers the barrier to entry, and it creates an upgrade path. Keep it useful but limited enough that growing users will naturally outgrow it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pro / Growth Tier
&lt;/h3&gt;

&lt;p&gt;This is your money-maker. Design everything on the page to guide users toward this plan. Use visual emphasis, a "Most Popular" badge, and position it in the center.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enterprise Tier
&lt;/h3&gt;

&lt;p&gt;The enterprise tier serves a dual purpose: it captures large customers, and it makes your Pro tier look affordable by comparison. This is called price anchoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Annual Billing Toggle
&lt;/h2&gt;

&lt;p&gt;Always offer annual billing with a visible discount. The industry standard is 15-20% savings. Show the monthly equivalent price so users can easily compare.&lt;/p&gt;

&lt;p&gt;Display the annual savings prominently — "Save $60/year" is more compelling than "Save 17%."&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Comparison Table
&lt;/h2&gt;

&lt;p&gt;A comparison table removes guesswork. Users can see exactly what they get at each tier without reading paragraphs of text.&lt;/p&gt;

&lt;p&gt;Best practices for comparison tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List features in order of importance&lt;/li&gt;
&lt;li&gt;Use checkmarks and X marks for clarity&lt;/li&gt;
&lt;li&gt;Bold the differentiating features&lt;/li&gt;
&lt;li&gt;Keep it under 15 rows to avoid overwhelming users&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Social Proof
&lt;/h2&gt;

&lt;p&gt;Place testimonials near your pricing cards. Quotes from customers who specifically mention value or ROI are most effective.&lt;/p&gt;

&lt;p&gt;Star ratings, customer count badges, and logo clouds all increase trust at the decision point.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ Section
&lt;/h2&gt;

&lt;p&gt;Address the top objections that prevent conversion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Can I cancel anytime?" — reduces commitment anxiety&lt;/li&gt;
&lt;li&gt;"Is there a free trial?" — lowers perceived risk&lt;/li&gt;
&lt;li&gt;"What happens when I exceed limits?" — removes uncertainty&lt;/li&gt;
&lt;li&gt;"Do you offer refunds?" — builds trust&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Money-Back Guarantee
&lt;/h2&gt;

&lt;p&gt;A 30-day guarantee removes the last barrier to purchase. The small number of refund requests is far outweighed by the increased conversion rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile Optimization
&lt;/h2&gt;

&lt;p&gt;Over 40% of SaaS pricing page visits happen on mobile. Your pricing cards must stack cleanly, and the comparison table needs horizontal scrolling or an accordion layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Too many tiers — stick to three&lt;/li&gt;
&lt;li&gt;No visual hierarchy — make the recommended plan obvious&lt;/li&gt;
&lt;li&gt;Hidden pricing — "Contact us for pricing" kills conversions for SMB products&lt;/li&gt;
&lt;li&gt;No annual option — you're leaving revenue on the table&lt;/li&gt;
&lt;li&gt;Missing FAQ — unaddressed objections kill deals&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test Everything
&lt;/h2&gt;

&lt;p&gt;A/B test your pricing page regularly. Small changes to price points, tier names, feature ordering, and CTA copy can have outsized effects on conversion.&lt;/p&gt;

&lt;p&gt;Build your pricing page with &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;Craftly's pricing template&lt;/a&gt; — conversion-optimized, responsive, and ready to deploy.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://getcraftly.dev/blog/saas-pricing-page-best-practices" rel="noopener noreferrer"&gt;Craftly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Production-ready Next.js templates — browse the current catalog at &lt;a href="https://getcraftly.gumroad.com" rel="noopener noreferrer"&gt;https://getcraftly.gumroad.com&lt;/a&gt; or save with the &lt;a href="https://getcraftly.gumroad.com/l/all-access-bundle" rel="noopener noreferrer"&gt;All-Access Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built with Next.js 16, TypeScript, and Tailwind CSS v4.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>design</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
