<?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: Genglin Zheng</title>
    <description>The latest articles on DEV Community by Genglin Zheng (@genglin-bulkpictools).</description>
    <link>https://dev.to/genglin-bulkpictools</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%2F2654065%2Fd7a7b773-7671-4953-ad37-cf07f3a8e1e8.png</url>
      <title>DEV Community: Genglin Zheng</title>
      <link>https://dev.to/genglin-bulkpictools</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/genglin-bulkpictools"/>
    <language>en</language>
    <item>
      <title>My side project just had its best month ever. I have no idea why. Here's what happened.</title>
      <dc:creator>Genglin Zheng</dc:creator>
      <pubDate>Mon, 30 Mar 2026 12:50:00 +0000</pubDate>
      <link>https://dev.to/genglin-bulkpictools/my-side-project-just-had-its-best-month-ever-i-have-no-idea-why-heres-what-happened-353j</link>
      <guid>https://dev.to/genglin-bulkpictools/my-side-project-just-had-its-best-month-ever-i-have-no-idea-why-heres-what-happened-353j</guid>
      <description>&lt;p&gt;Okay so this is a little embarrassing to admit —&lt;/p&gt;

&lt;p&gt;I launched bulkpictools.com three months ago and genuinely forgot to check &lt;br&gt;
the analytics for like two weeks straight.&lt;/p&gt;

&lt;p&gt;When I finally opened Google Search Console, I had to look twice.&lt;/p&gt;

&lt;p&gt;Month 3 traffic was more than Month 1 and Month 2 put together.&lt;/p&gt;

&lt;p&gt;I'm not going to pretend I have a clean explanation for this. I did some SEO &lt;br&gt;
work around the end of Month 2 — rewrote a bunch of meta descriptions, cleaned &lt;br&gt;
up the page content so it actually matched what people were searching for, &lt;br&gt;
stopped being lazy about alt text. Basic stuff. The kind of stuff you tell &lt;br&gt;
yourself you'll do later and then actually do later.&lt;/p&gt;

&lt;p&gt;Did that cause the jump? Maybe. Probably? Google took its time, which, honestly, &lt;br&gt;
very on-brand for Google.&lt;/p&gt;



&lt;p&gt;But while I was waiting for the SEO stuff to kick in (or not), I shipped &lt;br&gt;
something that I've been wanting to build for a while.&lt;/p&gt;

&lt;p&gt;A background remover. Except the whole model runs in the browser.&lt;/p&gt;

&lt;p&gt;No server. Zero backend calls. The image never goes anywhere.&lt;/p&gt;

&lt;p&gt;I know "client-side AI" sounds like it should be complicated but the actual &lt;br&gt;
core of it is pretty straightforward once you get ONNX Runtime Web set up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InferenceSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./model.onnx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tensor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;preprocessImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tensor&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;applyAlphaMask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originalImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiup798lnqodrrfohccb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiup798lnqodrrfohccb.png" alt="auto removing background" width="800" height="391"&gt;&lt;/a&gt;&lt;br&gt;
The annoying part wasn't the model. It was memory. When someone tries to &lt;br&gt;
process 10 high-res photos back to back, things get ugly fast. Still working &lt;br&gt;
through that. Releasing &amp;gt; perfect, etc.&lt;/p&gt;




&lt;p&gt;The thing nobody mentions about client-side inference is how weird it feels &lt;br&gt;
the first time it actually works. You drag in a photo, the model loads, &lt;br&gt;
and suddenly the background is just... gone. Locally. No spinner waiting &lt;br&gt;
on a server response. It's fast in a way that feels slightly wrong.&lt;/p&gt;

&lt;p&gt;Privacy-wise it's also just cleaner. I don't have to store anything, &lt;br&gt;
process anything server-side, or explain to users where their photos go. &lt;br&gt;
They go nowhere. That's the whole point.&lt;/p&gt;




&lt;p&gt;Quick note on how this actually gets built:&lt;/p&gt;

&lt;p&gt;I have a full-time job. This entire project gets worked on during my commute — &lt;br&gt;
phone in one hand, trying not to miss my stop with the other. Most of the &lt;br&gt;
testing happens on my phone screen, which is a chaotic way to do QA but &lt;br&gt;
also weirdly effective because if it works on a moving subway, it works.&lt;/p&gt;

&lt;p&gt;I'm not romanticizing the grind or whatever. It's just the actual situation. &lt;br&gt;
You ship with the time you have.&lt;/p&gt;




&lt;p&gt;Anyway. Month 4 starts now. Continuing to validate the AI tools, &lt;br&gt;
fix what's broken, and figure out which features people actually use &lt;br&gt;
vs which ones I thought they'd use (always humbling).&lt;/p&gt;

&lt;p&gt;If you've built anything client-side AI recently — especially anything &lt;br&gt;
dealing with image processing in the browser — drop a comment. &lt;br&gt;
I'm curious what models you're running and what memory headaches &lt;br&gt;
you've run into.&lt;/p&gt;

&lt;p&gt;bulkpictools.com — go break something and tell me what breaks.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Introducing BulkPicTools:Why Your Images Belong on Your Hard Drive, Not the Cloud</title>
      <dc:creator>Genglin Zheng</dc:creator>
      <pubDate>Thu, 26 Feb 2026 10:36:49 +0000</pubDate>
      <link>https://dev.to/genglin-bulkpictools/introducing-bulkpictoolswhy-your-images-belong-on-your-hard-drive-not-the-cloud-59mj</link>
      <guid>https://dev.to/genglin-bulkpictools/introducing-bulkpictoolswhy-your-images-belong-on-your-hard-drive-not-the-cloud-59mj</guid>
      <description>&lt;h2&gt;
  
  
  The Cloud is a Lie (And I learned it the hard way in 2025)
&lt;/h2&gt;

&lt;p&gt;Let's be real for a second. For the last decade, we have been conditioned to think that for a tool to be powerful, it has to happen on a server. We have been told that to resize a hundred photos, we need to hand over our files to a cloud provider and hope their privacy policy is more than just marketing fluff.&lt;/p&gt;

&lt;p&gt;I used to believe that too. Until June 2025. &lt;/p&gt;

&lt;p&gt;I was working on a high-stakes project with over 200 sensitive client photos. I used a popular "cloud-based" batch converter because I thought it was easier. Halfway through the 400MB upload, the server glitched. Not only did the conversion fail, but the site refreshed, and my original upload queue vanished. Even worse, three days later, I started seeing "targeted ads" related to the content of those private photos. That was my wake-up call. The cloud isn't just a convenience; it's a security hole you are paying to fall into.&lt;/p&gt;

&lt;p&gt;But it is 2026. Your browser is no longer just a window for text; it is a high-performance engine. Your computer GPU is sitting there, bored, waiting for something to do. &lt;/p&gt;

&lt;p&gt;This is why we built &lt;strong&gt;&lt;a href="https://bulkpictools.com/" rel="noopener noreferrer"&gt;BulkPicTools&lt;/a&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;We did not just build another image editor. We built a manifesto for the modern web. A place where batch processing and privacy finally live in the same room. The era of uploading your life just to change a file extension is officially dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Rant: Why is "Simple" Image Editing so Broken?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the tech, can we talk about how annoying "online tools" have become? &lt;/p&gt;

&lt;p&gt;You find a site that promises to resize a photo. You click "Upload," and suddenly you are hit with a popup: &lt;em&gt;"Please create an account to see your results."&lt;/em&gt; You give them your email, wait for a verification code that never arrives, and finally, when you get back in, you see a giant "PREMIUM ONLY" watermark over your image. &lt;/p&gt;

&lt;p&gt;Or my personal favorite: The tools that make you watch a 30-second ad for a mobile game just to download a single 100KB WebP. &lt;/p&gt;

&lt;p&gt;It is exhausting. It is disrespectful to your time. And frankly, it is predatory. At BulkPicTools, we have a simple rule: No accounts. No watermarks. No "waiting in line" for a server to process your file. You open the page, you drop your images, and you are done. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Renaissance: Why Local-First is the Only Way Forward
&lt;/h2&gt;

&lt;p&gt;Most online tools are black boxes. You send your data in, something happens on a server you do not own, and a file comes back. But what happens to the copy on their server? Who is training their AI models on your proprietary designs? &lt;/p&gt;

&lt;p&gt;Actually, the security risks of cloud-side processing are growing every day. Metadata leakage and server-side logs are weekly headlines.&lt;/p&gt;

&lt;p&gt;BulkPicTools is fundamentally different. When you use our &lt;strong&gt;&lt;a href="https://bulkpictools.com/" rel="noopener noreferrer"&gt;Bulk Image Editor&lt;/a&gt;&lt;/strong&gt;, the processing happens inside your browser RAM and CPU. 100% of the mathematical operations are done locally. If you unplug your internet cable halfway through, the tool keeps working. Why? Because your images belong to you, and they should never leave your sight. This local-first architecture is not just a feature; it is a philosophical commitment to the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Magic Under the Hood: WebAssembly &amp;amp; GPU Acceleration
&lt;/h3&gt;

&lt;p&gt;How can a website be faster than a professional desktop app? The answer is &lt;strong&gt;WebAssembly (WASM)&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In the past, browsers were limited by JavaScript—great for clicking buttons, terrible for crunching pixels. We compiled low-level C++ and Rust libraries into a binary format that your browser executes at near-native speeds. &lt;/p&gt;

&lt;p&gt;When you drag 500 high-resolution photos into our &lt;strong&gt;&lt;a href="https://bulkpictools.com/tools/resize/image-resizer" rel="noopener noreferrer"&gt;Image Resizer&lt;/a&gt;&lt;/strong&gt;, we trigger a multi-threaded, GPU-accelerated pipeline. We utilize SIMD (Single Instruction, Multiple Data) instructions to process multiple pixels at once. It is the reason we can crunch through a gigabyte of images while the other guys are still "Waiting for Upload."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.bulkpictools.com%2Fblog%2Fbulkpictools-local-first.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.bulkpictools.com%2Fblog%2Fbulkpictools-local-first.webp" title="The future is local: Professional power without the server lag." alt="A futuristic workstation showing a user processing thousands of images with glowing data streams." width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Full Ecosystem for the Modern Content Creator
&lt;/h2&gt;

&lt;p&gt;We did not stop at just resizing. We looked at the daily friction points for photographers, developers, and e-commerce managers. &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Precision Resizing without the Blur
&lt;/h3&gt;

&lt;p&gt;Most resizers use cheap algorithms that leave your photos looking soft. We use high-fidelity Bicubic and Lanczos interpolation. Whether you are prepping for a 4K display or a Shopify store, our &lt;strong&gt;&lt;a href="https://bulkpictools.com/tools/resize/image-resizer" rel="noopener noreferrer"&gt;Image Resizer&lt;/a&gt;&lt;/strong&gt; ensures every pixel is exactly where it should be. We give you granular control over padding and background fills—perfect for that "Instagram Frame" look without the manual work in Photoshop.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Great Format Migration: Mastering WebP &amp;amp; AVIF
&lt;/h3&gt;

&lt;p&gt;In 2026, if you are still using 5MB JPEGs on your blog, you are losing money. Large images kill your &lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt;. Our &lt;strong&gt;&lt;a href="https://bulkpictools.com/tools/convert/image-converter" rel="noopener noreferrer"&gt;Image Converter&lt;/a&gt;&lt;/strong&gt; makes the transition to next-gen formats seamless. &lt;/p&gt;

&lt;p&gt;By switching to AVIF, you can often see a 90% reduction in file size without any perceptible loss in quality. This isn't just about saving storage; it's about making your site load instantly on a mobile device in the middle of a subway station.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Controlling Your Digital Shadow with EXIF Editing
&lt;/h3&gt;

&lt;p&gt;Every photo you take has a digital fingerprint. This EXIF data contains your GPS coordinates and even your camera serial number. &lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;&lt;a href="https://bulkpictools.com/tools/exif/exif-editor" rel="noopener noreferrer"&gt;EXIF Editor&lt;/a&gt;&lt;/strong&gt; gives you the power to strip this data entirely. Before you post that "For Sale" ad on a marketplace, you should probably make sure your home address isn't embedded in the metadata. BulkPicTools makes it easy to sanitize hundreds of photos at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive: The Performance Gap
&lt;/h2&gt;

&lt;p&gt;Let's talk numbers. We ran a benchmark against three of the most popular "cloud-based" image converters. The task: resize and convert 50 high-res JPEGs (average 8MB per file).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Tool A&lt;/strong&gt;: 4 minutes and 12 seconds (Upload/Download latency).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Tool B&lt;/strong&gt;: 5 minutes and 45 seconds (Throttled free tier).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BulkPicTools&lt;/strong&gt;: &lt;strong&gt;14 seconds&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bottleneck in the cloud model isn't the processing—it's the pipe. Moving 400MB of data to a server and back will always be slower than processing it in your machine's local memory. We cut out the middleman.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Developer’s Perspective: How WASM is Changing the Web
&lt;/h2&gt;

&lt;p&gt;For years, developers were forced to make a compromise. If you wanted to build a powerful photo application, you had to build a desktop app (C++, Swift, or Java). If you wanted the reach of the web, you had to settle for slow, JavaScript-based processing or expensive server-side APIs. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebAssembly (WASM)&lt;/strong&gt; has shattered that wall. By compiling low-level C++ libraries into a binary format that the browser can execute at nearly the speed of a local machine, we have turned the web browser into a professional-grade execution environment. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why our architecture is "Zero-Knowledge"
&lt;/h3&gt;

&lt;p&gt;From a security engineering standpoint, BulkPicTools follows a "Zero-Knowledge" philosophy. Because we never see your data, we cannot leak it. In an era of strict GDPR and CCPA regulations, using a local-first tool like ours isn't just a personal preference—it is a smart business move. Developers can use our tool to prep assets for their projects without ever worrying about their company's proprietary designs hitting a third-party server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The E-commerce Efficiency Gap: A Real-World Use Case
&lt;/h2&gt;

&lt;p&gt;Let's look at a typical day for a Shopify or Amazon seller in 2026. You just received 500 product photos from a professional photographer. They are high-resolution, 50MB TIFF files.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4e28w3pl68q67vyd3sq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4e28w3pl68q67vyd3sq.webp" alt="The E-commerce Efficiency Gap: A Real-World Use Case" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Old Way (Cloud-based):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a browser and find a batch converter.&lt;/li&gt;
&lt;li&gt;Attempt to upload 25GB of data. &lt;/li&gt;
&lt;li&gt;The browser crashes halfway through because of a timeout.&lt;/li&gt;
&lt;li&gt;You try again in smaller batches. Total time wasted: 3 hours.&lt;/li&gt;
&lt;li&gt;You pay a "Premium Subscription" because the free tier limits you to 10 files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The BulkPicTools Way:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open our &lt;strong&gt;&lt;a href="https://bulkpictools.com/" rel="noopener noreferrer"&gt;Bulk Image Editor&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Drag and drop all 500 files.&lt;/li&gt;
&lt;li&gt;Your local CPU cores light up. Our multi-threaded engine starts processing 8 to 16 images simultaneously.&lt;/li&gt;
&lt;li&gt;Within 5 minutes, all 500 files are resized to 1200px, converted to WebP, and renamed.&lt;/li&gt;
&lt;li&gt;You download a single ZIP file or individual images. Total time: 5 minutes. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the "Efficiency Gap." For businesses, time is literally money. BulkPicTools reclaims those hours so you can focus on selling, not waiting for an upload bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond Simple Edits: The Future of Browser-Based AI
&lt;/h2&gt;

&lt;p&gt;As we move deeper into 2026, the horizon for what is possible in a browser is expanding. We are currently testing &lt;strong&gt;Local AI Models&lt;/strong&gt; (using WebGPU) that will allow for even more advanced features without sacrificing our privacy-first mission.&lt;/p&gt;

&lt;p&gt;Imagine being able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove Backgrounds Locally&lt;/strong&gt;: No more sending your photos to an AI server to remove a background. Our upcoming tools will use your computer's NPU (Neural Processing Unit) to mask objects in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Upscaling&lt;/strong&gt;: Using local super-resolution models to turn a blurry 400px thumbnail into a crisp 1600px hero image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-Color Correction&lt;/strong&gt;: Batch adjusting the white balance and exposure of 1,000 photos based on a single reference image—all happening on your device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We aren't just building a tool for today; we are building a platform for the future of decentralized, private, and high-performance creative work.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  1. Is BulkPicTools really free?
&lt;/h3&gt;

&lt;p&gt;Yes. Because we do not have massive server bills to pay (since your computer does the work), we can offer a powerful free tier that beats most paid competitors. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Does it work on my phone?
&lt;/h3&gt;

&lt;p&gt;Absolutely. Modern smartphones have incredibly powerful processors. You can batch resize or convert photos directly in your mobile Safari or Chrome browser while on the go.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What about browser compatibility?
&lt;/h3&gt;

&lt;p&gt;We support all modern browsers that handle WebAssembly, including Chrome, Firefox, Edge, and Safari. If your browser is updated to a version from the last 2-3 years, you are good to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Is there a file size limit?
&lt;/h3&gt;

&lt;p&gt;The only limit is your computer’s RAM. We have successfully tested batches of over 1,000 images and individual files as large as 200MB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts: Joining the Privacy Revolution
&lt;/h2&gt;

&lt;p&gt;The internet is at a crossroads. We can continue down the path of centralized "Cloud Everything," where our data is a commodity used to train corporate models, or we can choose a local-first future. &lt;/p&gt;

&lt;p&gt;BulkPicTools is our contribution to that second path. It is a fast, free, and uncompromisingly private way to manage your digital life. Whether you are a professional photographer, a web developer, or someone just trying to clear up space on their phone, we are here to make your workflow smoother.&lt;/p&gt;

&lt;p&gt;Stop waiting for the cloud. Start processing at the speed of your own hardware.&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://bulkpictools.com/" rel="noopener noreferrer"&gt;Try BulkPicTools for free today&lt;/a&gt;&lt;/strong&gt; and reclaim your digital sovereignty.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>startup</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Nuxt 4 War Stories: The Errors I Never Want to See Again</title>
      <dc:creator>Genglin Zheng</dc:creator>
      <pubDate>Thu, 29 Jan 2026 15:55:08 +0000</pubDate>
      <link>https://dev.to/genglin-bulkpictools/nuxt-4-war-stories-the-errors-i-never-want-to-see-again-3iig</link>
      <guid>https://dev.to/genglin-bulkpictools/nuxt-4-war-stories-the-errors-i-never-want-to-see-again-3iig</guid>
      <description>&lt;p&gt;&lt;strong&gt;Nuxt 4 + Tailwind + Cloudflare.&lt;/strong&gt; Sounds like the dream stack, right?&lt;/p&gt;

&lt;p&gt;When I decided to rebuild &lt;strong&gt;&lt;a href="https://bulkpictools.com" rel="noopener noreferrer"&gt;BulkPicTools&lt;/a&gt;&lt;/strong&gt;, that’s exactly what I thought. "Nuxt 4 is built for speed," I told myself. "This is going to be an easy win."&lt;/p&gt;

&lt;p&gt;Well, reality hit me pretty hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honestly, this is the "Bleeding Edge Tax."&lt;/strong&gt; Everything runs perfectly on &lt;code&gt;localhost&lt;/code&gt;, but the moment you try to deploy, things start breaking. Since I’ve already spent the sleepless nights fixing this stuff, I’m writing it down so you (hopefully) don’t have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. That Lighthouse Score That Hurt My Feelings
&lt;/h2&gt;

&lt;p&gt;When I deployed the first version, I was feeling pretty good. The UI was snappy, Tailwind was doing its job. I ran a Lighthouse test just to see that sweet 100/100.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: &amp;lt; 60.&lt;/strong&gt; 🔴&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9087jpga1mwkr1h2hcaf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9087jpga1mwkr1h2hcaf.webp" alt="Nuxt 4 Initial PageSpeed Score with Tailwind CSS Issues" width="800" height="394"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;(Caption: Ouch. The initial mobile performance score was a disaster.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was shocked. Nuxt 4 is supposed to be a performance beast!&lt;/p&gt;

&lt;p&gt;I stared at the screen for a solid minute. Isn't Nuxt 4 supposed to be a performance beast?&lt;/p&gt;

&lt;p&gt;Turns out, &lt;strong&gt;it was the CSS.&lt;/strong&gt; By default, the build was generating a bunch of separate CSS files for Tailwind. The browser had to wait for all those network requests before it could paint anything. My First Contentful Paint (FCP) was trash.&lt;/p&gt;

&lt;p&gt;The fix was a bit aggressive: &lt;strong&gt;I forced the styles directly into the HTML.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I stopped using &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks in my Vue components entirely (strict discipline required) and forced Nuxt to inline the Tailwind config.&lt;/p&gt;

&lt;p&gt;Here is the "magic switch" I added to &lt;code&gt;nuxt.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Centralize all Tailwind styles here&lt;/span&gt;
  &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/assets/css/tailwind.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This is the lifesaver: inline the styles to kill network requests&lt;/span&gt;
    &lt;span class="na"&gt;inlineStyles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;After this? The score shot back up to 90+. It feels a bit weird to inline everything in 2025, but hey, if it makes the site fast, I’ll take it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92xrlir5t04poiwjwxnd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92xrlir5t04poiwjwxnd.webp" alt="Nuxt 4 PageSpeed Insight score showing 90+ on mobile after optimization" width="800" height="389"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Finally seeing green. The "Inline Styles" config brought the mobile score from &amp;lt;60 back to 99.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. The "It Works on My Machine" Nightmare (Cloudflare Edition)
&lt;/h2&gt;

&lt;p&gt;This is the sentence every developer dreads.&lt;/p&gt;

&lt;p&gt;I chose Cloudflare Pages because it’s free and fast. Locally, &lt;code&gt;npm run generate&lt;/code&gt; worked flawlessly. I pushed to GitHub, poured a coffee, and waited for the green checkmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Failed.&lt;/strong&gt; 💥&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n86mhcn20ueg1nhcz8l.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n86mhcn20ueg1nhcz8l.webp" alt="Cloudflare Pages Build Error Log for Nuxt 4" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error was complaining about a missing &lt;code&gt;queryCollection&lt;/code&gt;. This is a core function I use to fetch blog content. It turns out Cloudflare’s build environment handles module resolution differently than my local Mac.&lt;/p&gt;

&lt;p&gt;Did I have time to debug Cloudflare's internal Node environment? No.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, I decided to "lie" to the compiler.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wrote a Mock Adapter to trick the build process into thinking those modules existed.&lt;/p&gt;

&lt;p&gt;First, I aliased the missing modules in the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fileURLToPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Can't find it? Redirect to my fake file.&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxt/content/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./adapter-content.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxt/content/dist/module.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./adapter-content.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&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;Then, I created the "liar" file, &lt;code&gt;adapter-content.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// adapter-content.ts&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Mock queryCollection for the Content v3 API&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([]),&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Mock the legacy API too, just in case the Sitemap module asks for it&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serverQueryContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([]),&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;queryCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;serverQueryContent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is it a dirty hack? Yes. Did it work? Absolutely. The build passed, and the site went live. Good enough for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Launching a Landing Page in 30 Minutes? You Can't Code That by Hand
&lt;/h2&gt;

&lt;p&gt;My plan is to launch dozens of specific tools, like "WebP to JPG" or "HEIC to PNG."&lt;/p&gt;

&lt;p&gt;If I had to manually code the Title, Meta Description, JSON-LD Schema, and i18n support for every single one... I’d be stuck writing boilerplate code for the rest of my life. Plus, trends move fast. If I see a keyword opportunity, I need that page live in &lt;strong&gt;under 30 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So I stopped writing pages and started writing config.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I built a &lt;strong&gt;Configuration-Driven Engine&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Config&lt;/strong&gt;: Everything is a JSON object. Input format, output format, FAQs, titles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Template&lt;/strong&gt;: One generic &lt;code&gt;.vue&lt;/code&gt; file that reads the config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The SEO Layer&lt;/strong&gt;: This is the secret sauce. The template automatically generates complex JSON-LD based on the config.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foym13cwoz93rq1b68cgw.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foym13cwoz93rq1b68cgw.webp" alt="Programmatic SEO Architecture: From JSON Config to Generated Landing Page" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Breadcrumbs? Generated from the route.&lt;br&gt;
Tool Schema? Filled in automatically.&lt;br&gt;
Translations? It looks up the keys based on the ID.&lt;/p&gt;

&lt;p&gt;Now, if I want to launch a new tool, I just commit a few lines of JSON. That’s how it should be.&lt;/p&gt;




&lt;p&gt;Building with bleeding-edge tech like Nuxt 4 is a bit of a rollercoaster. You get the incredible performance, but you also have to be ready to patch the holes in the ecosystem yourself.&lt;/p&gt;

&lt;p&gt;Is the code perfect? Definitely not. But &lt;strong&gt;BulkPicTools&lt;/strong&gt; is live, it loads instantly, and I didn't lose &lt;em&gt;all&lt;/em&gt; my hair building it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to see if my hacks held up? Go check it out:&lt;/strong&gt;&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://bulkpictools.com" rel="noopener noreferrer"&gt;BulkPicTools.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>I spent a year chasing AI hype and buying courses. Here is why I went back to Vue.</title>
      <dc:creator>Genglin Zheng</dc:creator>
      <pubDate>Mon, 19 Jan 2026 10:29:31 +0000</pubDate>
      <link>https://dev.to/genglin-bulkpictools/i-spent-a-year-chasing-ai-hype-and-buying-courses-here-is-why-i-went-back-to-vue-37l0</link>
      <guid>https://dev.to/genglin-bulkpictools/i-spent-a-year-chasing-ai-hype-and-buying-courses-here-is-why-i-went-back-to-vue-37l0</guid>
      <description>&lt;p&gt;I’ll be honest: I got lost.&lt;/p&gt;

&lt;p&gt;For the past year, I abandoned my comfort zone. As a veteran Vue 2 developer, I felt the "Fear Of Missing Out" (FOMO) hitting me hard. Everywhere I looked, people were shouting: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"AI will replace you!"&lt;/em&gt; &amp;gt; &lt;em&gt;"Learn LLMs or become obsolete!"&lt;/em&gt; &amp;gt; &lt;em&gt;"Make passive income with AI-generated videos!"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have two kids to feed. The anxiety was real.&lt;/p&gt;

&lt;p&gt;So, I did what many terrified developers did. I stopped writing code and started chasing the wind. I bought the expensive courses on "AI App Development". I tried to become a "Content Creator" using AI tools to churn out soulless videos. I tried to force myself to love the React ecosystem just because "it has more AI libraries."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I became a "Leek" (a term we use in China for people who get harvested by hype).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After a year of burning cash and time, I looked at my portfolio. I had zero real users, a bunch of "Hello World" AI demos, and a lingering feeling of emptiness. The AI tools improved my efficiency, sure, but they didn't solve the core problem: I wasn't building anything valuable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Return to Reality
&lt;/h2&gt;

&lt;p&gt;I had a reality check one late night. I realized that while everyone is trying to build the next ChatGPT wrapper, regular people still struggle with basic things.&lt;/p&gt;

&lt;p&gt;I create content, and I work with images daily. I realized that simple tasks like bulk resizing, converting formats, or compressing images without uploading them to a shady server (privacy matters!) are still annoying. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Photoshop is too expensive and heavy for quick tasks.&lt;/li&gt;
&lt;li&gt;Most online tools are ad-ridden nightmares that steal your data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, I made a decision. &lt;strong&gt;Stop chasing the hype. Start shipping utility.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From Vue 2 to Nuxt 4
&lt;/h2&gt;

&lt;p&gt;I went back to my roots, but I leveled up. I skipped the legacy Vue 2 patterns I was used to and dove straight into &lt;strong&gt;Vue 3 (Composition API)&lt;/strong&gt; and &lt;strong&gt;Nuxt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It was refreshing. No more fighting with Python environments or prompt engineering. Just pure, clean JavaScript/TypeScript.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://bulkpictools.com" rel="noopener noreferrer"&gt;BulkPicTools&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s not an "AI-powered, Blockchain-backed, Metaverse-ready" platform. It’s a boring, useful tool.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡️ &lt;strong&gt;Client-side only&lt;/strong&gt;: It runs entirely in your browser using WebAssembly and modern JS APIs.&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Privacy-first&lt;/strong&gt;: Your photos never leave your device. No server uploads.&lt;/li&gt;
&lt;li&gt;🛠 &lt;strong&gt;Solves a real problem&lt;/strong&gt;: Bulk processing for creators who value their time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I'm sharing this
&lt;/h2&gt;

&lt;p&gt;I’m writing this for any other developer who feels inadequate because they aren't an "AI Expert" yet.&lt;/p&gt;

&lt;p&gt;You don't need to ride every wave. Your engineering skills—whether it's Vue, Rails, or PHP—are still valuable if you use them to solve real problems.&lt;/p&gt;

&lt;p&gt;I’m still learning the ropes with the latest Nuxt features, and the journey from "Corporate Employee" to "Indie Hacker" is terrifying. But at least now, I'm building something real again.&lt;/p&gt;

&lt;p&gt;If you have feedback on the tool or the Nuxt implementation, I’d love to hear it! &lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>nucleart</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Next.js Multilingual Sitemap Optimization: Dodge Redirect Issues &amp; Boost SEO</title>
      <dc:creator>Genglin Zheng</dc:creator>
      <pubDate>Mon, 21 Apr 2025 01:38:37 +0000</pubDate>
      <link>https://dev.to/genglin-bulkpictools/nextjs-multilingual-sitemap-optimization-dodge-redirect-issues-boost-seo-3b73</link>
      <guid>https://dev.to/genglin-bulkpictools/nextjs-multilingual-sitemap-optimization-dodge-redirect-issues-boost-seo-3b73</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Ever been annoyed by this? You put in the work to generate a sitemap.xml for your multilingual site, only to check it on Ahrefs or Google Search Console and—bam!—a ton of “redirect issue” warnings. Take my site, &lt;code&gt;solomakerstudio.com&lt;/code&gt;, for example. Links without a language code (like &lt;code&gt;solomakerstudio.com/&lt;/code&gt;) all 301 redirect to the default language (say, &lt;code&gt;solomakerstudio.com/en/&lt;/code&gt;). It’s not just annoying—it can make search engines think your site’s messy, hurting your SEO.&lt;/p&gt;

&lt;p&gt;No stress, though! This article’s gonna walk you through optimizing your multilingual sitemap.xml to squash those redirect issues and make your site a search engine’s best friend. It’s super practical, beginner-friendly, and even if you’re new to SEO, you’ll be able to follow along!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Does sitemap.xml Have Redirect Issues?
&lt;/h2&gt;

&lt;p&gt;Let’s get to the root of the problem. Many multilingual sites (using subdirectories like &lt;code&gt;/en&lt;/code&gt; or &lt;code&gt;/zh&lt;/code&gt;) set non-localized links (e.g., &lt;code&gt;solomakerstudio.com/&lt;/code&gt;) to auto-redirect to a default language (like &lt;code&gt;/en/&lt;/code&gt;). That’s fine for users, but it causes headaches in sitemap.xml:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Search engines hate redirects&lt;/strong&gt;: If &lt;code&gt;solomakerstudio.com/&lt;/code&gt; is in your sitemap but redirects to &lt;code&gt;/en/&lt;/code&gt;, crawlers have to jump through an extra hoop. It slows them down and makes your sitemap look less “direct.”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO tools throw warnings&lt;/strong&gt;: Tools like Ahrefs or SEMrush flag redirects in your sitemap as “issues,” which is just frustrating.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate content risks&lt;/strong&gt;: If both &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/en/&lt;/code&gt; are in your sitemap, search engines might think they’re separate pages, diluting your page’s ranking power.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix? &lt;strong&gt;Only include final, non-redirecting URLs in your sitemap.xml&lt;/strong&gt;—no “middleman” links that trigger redirects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fza90kcdp1jpb477brq3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fza90kcdp1jpb477brq3d.png" alt="Ahrefs showing 404 redirect errors" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4 Practical Steps to Optimize Your sitemap.xml
&lt;/h2&gt;

&lt;p&gt;Alright, let’s get to the good stuff! Here are four dead-simple steps to make your multilingual sitemap clean and SEO-friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Only Include Localized Links
&lt;/h3&gt;

&lt;p&gt;The golden rule: &lt;strong&gt;Don’t put non-localized links&lt;/strong&gt; (like &lt;code&gt;solomakerstudio.com/&lt;/code&gt;) in your sitemap.xml. Only include URLs with language subdirectories, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://solomakerstudio.com/en/&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2025-04-17&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;weekly&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.8&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://solomakerstudio.com/zh/&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2025-04-17&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;weekly&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.8&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to do it?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CMS users&lt;/strong&gt; (e.g., WordPress): Check your sitemap plugin (like Yoast SEO or Rank Math) settings to ensure it only generates URLs with language codes (e.g., &lt;code&gt;/en/&lt;/code&gt; or &lt;code&gt;/zh/&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom scripts&lt;/strong&gt;: If you’re generating your sitemap manually, tweak the code to filter out non-localized URLs. For a Next.js project, add a &lt;code&gt;sitemap.tsx&lt;/code&gt; file in the root and use this script:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/appConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Helper function to generate localized URLs&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLocalizedUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;localePath&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;routePath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sitemap&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Define static routes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;staticRoutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;changeFrequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;daily&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;changeFrequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;daily&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;sitemapData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sitemap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="c1"&gt;// Get supported languages&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Loop through languages to generate localized links&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;staticRoutes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;sitemapData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getLocalizedUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;changeFrequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeFrequency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// All generated links include language prefixes&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sitemapData&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;&lt;strong&gt;Pro tip&lt;/strong&gt;: This applies to your homepage too! If &lt;code&gt;solomakerstudio.com/&lt;/code&gt; redirects to &lt;code&gt;solomakerstudio.com/en/&lt;/code&gt;, only include the &lt;code&gt;/en/&lt;/code&gt; version in your sitemap.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Add hreflang Tags to Clarify Language Versions
&lt;/h3&gt;

&lt;p&gt;For multilingual sites, you gotta tell search engines which pages are the same content in different languages. That’s where &lt;code&gt;hreflang&lt;/code&gt; tags come in. Add them to your sitemap.xml like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://solomakerstudio.com/en/&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://solomakerstudio.com/en/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"zh"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://solomakerstudio.com/zh/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2025-04-17&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://solomakerstudio.com/zh/&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://solomakerstudio.com/en/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"zh"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://solomakerstudio.com/zh/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2025-04-17&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why bother?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tells Google which page is English, which is Chinese, preventing duplicate content issues.
&lt;/li&gt;
&lt;li&gt;Improves user experience by helping search engines serve the right language version based on user preferences.
&lt;/li&gt;
&lt;li&gt;Saves crawler resources—search engines have tons of sites to crawl, so making their job easier helps your site get indexed faster!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to add them?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress users&lt;/strong&gt;: Plugins like Yoast SEO or Polylang can auto-generate sitemaps with &lt;code&gt;hreflang&lt;/code&gt; tags.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual sitemaps&lt;/strong&gt;: Make sure your sitemap XML includes the &lt;code&gt;xmlns:xhtml="http://www.w3.org/1999/xhtml"&lt;/code&gt; namespace, like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23wxatqve6gx6cq5ee9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23wxatqve6gx6cq5ee9d.png" alt="sitemap.xml with hreflang" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Ensure Redirects Use 301
&lt;/h3&gt;

&lt;p&gt;If non-localized links (like &lt;code&gt;solomakerstudio.com/&lt;/code&gt;) redirect, make sure they use &lt;strong&gt;301 permanent redirects&lt;/strong&gt;, not 302 temporary ones. Why? A 301 tells search engines, “This page has moved for good,” passing SEO juice smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your browser’s developer tools (F12 -&amp;gt; Network tab), visit &lt;code&gt;solomakerstudio.com/&lt;/code&gt;, and check if the status code is 301.
&lt;/li&gt;
&lt;li&gt;Or use an online tool like redirect-checker.org to confirm.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to fix?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server config&lt;/strong&gt; (Nginx/Apache): Ensure redirects are set to 301. For Nginx, it looks like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;  &lt;span class="k"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;^/&lt;/span&gt;$ &lt;span class="n"&gt;/en/&lt;/span&gt; &lt;span class="s"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CMS users&lt;/strong&gt;: Check your language-switching plugin to confirm it’s using 301 redirects.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js example&lt;/strong&gt; (e.g., &lt;code&gt;solomakerstudio.com&lt;/code&gt; hosted on Vercel): Use a custom &lt;code&gt;middleware.ts&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createMiddleware&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-intl/middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./lib/appConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intlMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;localePrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;always&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Handle root path (/) for 301 redirect based on Accept-Language&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acceptLanguage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accept-language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;targetLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Fallback&lt;/span&gt;
    &lt;span class="c1"&gt;// Match language based on Accept-Language header&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acceptLanguage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;targetLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acceptLanguage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;targetLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Perform 301 redirect&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redirectUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;301&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="nf"&gt;intlMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/((?!api|_next|.*&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;..*).*)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/rss.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:locale/rss.xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin/:path*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;h3&gt;
  
  
  Step 4: Validate Your sitemap.xml
&lt;/h3&gt;

&lt;p&gt;Before submitting your shiny new sitemap.xml, double-check it to avoid any oopsies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to validate?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Submit your sitemap to Google Search Console’s “Sitemaps” tool and check the “Coverage” report after a few days for errors.
&lt;/li&gt;
&lt;li&gt;Run Ahrefs’ Site Audit to see if there are any redirect warnings in your sitemap.
&lt;/li&gt;
&lt;li&gt;Locally, use an XML validator (like xmlvalidation.com) to ensure your sitemap’s format is correct.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: After submitting to Google Search Console, hit “Resubmit” to nudge crawlers to grab the updated version. Here’s what our optimized sitemap looked like after running it through Ahrefs:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkke914wfn8e5m2is42b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkke914wfn8e5m2is42b.png" alt="Ahrefs results after optimization" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Extra Tips to Keep in Mind
&lt;/h2&gt;

&lt;p&gt;A few bonus pointers to make your sitemap even better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic sitemaps&lt;/strong&gt;: If your site has tons of pages, use a script or plugin to auto-generate your sitemap—saves time!
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular updates&lt;/strong&gt;: Keep your sitemap fresh with accurate &lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt; dates whenever your site changes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multilingual plugins&lt;/strong&gt;: For WordPress, plugins like WPML or Polylang make multilingual sitemaps a breeze—set them up and relax.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Fixing your multilingual sitemap.xml isn’t rocket science. The key? &lt;strong&gt;Skip redirecting links, use localized URLs, add hreflang tags, and stick to 301 redirects.&lt;/strong&gt; Follow these four steps, and your sitemap will be squeaky clean—no more Ahrefs warnings, and Google’s crawlers will love your site.&lt;/p&gt;

&lt;p&gt;Give it a shot! If you hit any snags (like how to tweak your CMS or code), just let me know, and I’ll break it down for you!&lt;/p&gt;




</description>
      <category>nextjs</category>
      <category>seo</category>
      <category>sitemap</category>
      <category>react</category>
    </item>
    <item>
      <title>From Vue to Next.js: How I Let Go and Built a Curated Tool Hub 🌟</title>
      <dc:creator>Genglin Zheng</dc:creator>
      <pubDate>Tue, 01 Apr 2025 07:33:55 +0000</pubDate>
      <link>https://dev.to/genglin-bulkpictools/from-vue-to-nextjs-how-i-let-go-and-built-a-curated-tool-hub-1jig</link>
      <guid>https://dev.to/genglin-bulkpictools/from-vue-to-nextjs-how-i-let-go-and-built-a-curated-tool-hub-1jig</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;As a Vue frontend dev, I initially just wanted to whip up a simple static site with Next.js—learn a hot framework while I was at it. But guess what? Building from scratch took forever, totally clashing with my goal of helping indie devs ship fast! 😅 So, I pivoted hard, turning SoloMakerStudio.com into a dynamic, multilingual platform. This is my story—tech choices, struggles, and all—hoping it sparks something for you! ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It All Started
&lt;/h2&gt;

&lt;p&gt;I’ve always been a Vue fan, dabbling in React Native too. One night, I got this itch: create a site curating free tools to help indie devs turn ideas into reality, pronto. My first plan? A static Next.js site—simple, and a chance to level up my skills. But here’s the kicker: hand-coding pages and tweaking styles ate up so much time, my MVP was nowhere in sight. I started panicking—wasn’t this just a waste? 🤔&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shift from Static to Dynamic
&lt;/h2&gt;

&lt;p&gt;Once I saw the issue, I hit pause and thought: if I can’t launch fast, how can I help others do it? I scrapped the static idea and went dynamic. Next.js’s dynamic routing gave me flexibility, Supabase’s real-time database made tool suggestions snappy, and Vercel’s one-click deploys saved my sanity! 🚀 Later, I tossed in multilingual support—shifting from “just messing around” to “actually serving users.” It was exhausting, but it got me closer to the mission! 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  Curating Only the Good Stuff
&lt;/h2&gt;

&lt;p&gt;After drowning in tool overload myself, I decided SoloMakerStudio wouldn’t be a chaotic pile of options. It’s all about free, practical tools indie devs can actually use—easy to pick up and quick to ship with. Think a lightweight prototyping gem that gets you live in two days, not some bloated framework you’d need a month to master. “Less is more” became my mantra, born from my own trial and error. 🌟 Ever felt paralyzed by too many choices?&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Hurdles and Wins
&lt;/h2&gt;

&lt;p&gt;Jumping from Vue to Next.js wasn’t a cakewalk—JSX drove me nuts, and server-side rendering had me spinning. 😵 Configuring Supabase and Vercel felt like a puzzle too. But every hurdle I cleared brought me closer to the goal. Now, Next.js lets me iterate fast, Supabase keeps things lively, and Vercel makes deploying as easy as hitting enter. Looking back, sticking to a static site would’ve left me stuck—probably still offline! 😂&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch Day Joy
&lt;/h2&gt;

&lt;p&gt;March 2025 rolled around, and SoloMakerStudio finally went live! 🎉 It’s not the static page I first pictured—it’s a dynamic tool hub tailored for indie devs. When the first user said, “Finally, a platform that doesn’t lose me!” I was over the moon. That’s when I knew the Vue-to-Next.js grind paid off. ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;SoloMakerStudio’s heart is “less is more”—giving you just the tools you need to go from idea to reality, fast. Check it out at &lt;a href="https://solomakerstudio.com" rel="noopener noreferrer"&gt;solomakerstudio.com&lt;/a&gt;! 🌈 Drop a comment: what’s tripped you up shipping an MVP? I’d love to hear your tales! And which free tools are must-haves for indie devs? Let’s chat! 😉&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>react</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
