<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dax&apos;s Blog</title><description>Tracking the progression from Front-End Engineer to Full-Stack Developer and Indie Hacker.</description><link>https://daolanx.me/</link><item><title>From Idea to Code: Using Stitch with Claude Code and Figma MCP</title><link>https://daolanx.me/en/posts/post-18/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-18/</guid><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;1-the-results&quot;&gt;1. The Results&lt;/h1&gt;
&lt;p&gt;First, let’s jump straight to the results. Since I started using &lt;a href=&quot;https://stitch.withgoogle.com/&quot;&gt;Stitch&lt;/a&gt;, the improvement in overall design quality has been massive.&lt;/p&gt;
&lt;h2 id=&quot;before-an-ai-chat-web-ui-built-manually-based-on-programmers-intuition&quot;&gt;Before: An AI Chat Web UI built manually based on “programmer’s intuition”&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/18/1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;after-implementation-using-stitch-combined-with-claude-code--figma-mcp&quot;&gt;After: Implementation using Stitch combined with Claude Code + Figma MCP&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/18/2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can check out the Live Demo here: &lt;a href=&quot;https://demo.daolanx.com/ai-chat&quot;&gt;https://demo.daolanx.com/ai-chat&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;2-the-process&quot;&gt;2. The Process&lt;/h1&gt;
&lt;h2 id=&quot;21-using-ai-in-stitch-to-assist-with-design-drafts&quot;&gt;2.1 Using AI in Stitch to Assist with Design Drafts&lt;/h2&gt;
&lt;p&gt;Using &lt;a href=&quot;https://stitch.withgoogle.com/&quot;&gt;Stitch&lt;/a&gt; is just like a regular chat. You input your ideas and descriptions, and it generates a corresponding design draft. You can keep refining it through conversation, and it will iterate on the design based on your feedback.
&lt;img src=&quot;/images/posts/18/3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;22-exporting-to-design-software-formats&quot;&gt;2.2 Exporting to Design Software Formats&lt;/h2&gt;
&lt;p&gt;Once you’re happy with a version, you can export it directly.
&lt;img src=&quot;/images/posts/18/4.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;23-design-to-code-implementation&quot;&gt;2.3 Design-to-Code Implementation&lt;/h2&gt;
&lt;p&gt;This part follows the same steps as my previous post, &lt;a href=&quot;https://www.daolanx.me/en/posts/post-16/&quot;&gt;Design-to-Code with Claude Code + Figma MCP&lt;/a&gt;, so I won’t repeat the details here.&lt;/p&gt;
&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;
&lt;p&gt;As you can see, the actual steps are quite simple. But when we evaluate production tools and workflows, it’s not just about simplicity. For a developer, the real value—and why I think it’s worth documenting—lies in the ability to close the loop on the pre-production design phase and seamlessly integrate it into a real MVP workflow:&lt;/p&gt;
&lt;h2 id=&quot;1-bridging-the-design-gap-for-developers&quot;&gt;1. Bridging the Design Gap for Developers&lt;/h2&gt;
&lt;p&gt;Most developers don’t have a strong design background. &lt;a href=&quot;https://stitch.withgoogle.com/&quot;&gt;Stitch&lt;/a&gt; goes a long way in filling that gap, effectively expanding what we are capable of building. It moves us beyond just being “design-to-code” translators or functional developers; now, we can take a raw idea and turn it into a functional MVP independently.&lt;/p&gt;
&lt;p&gt;Of course, this doesn’t mean Stitch replaces “good design” entirely. Much like how AI assists in coding, AI-driven design still relies heavily on the user’s product sense and taste. The precision and control in prompting—and knowing what looks right—will always create a gap between professional and amateur results.&lt;/p&gt;
&lt;h2 id=&quot;2-enable-integrating-into-the-mvp-workflow&quot;&gt;2. Enable Integrating into the MVP Workflow&lt;/h2&gt;
&lt;p&gt;There are many “skill + AI” solutions out there for generating code that attempts to mimic design. In comparison, &lt;a href=&quot;https://stitch.withgoogle.com/&quot;&gt;Stitch&lt;/a&gt; feels simpler and more stable because the stages are clearly defined: you handle the design during the design phase, and then move to pure design-to-code execution. For MVP-style products, this can be integrated into a production pipeline almost immediately.&lt;/p&gt;</content:encoded></item><item><title>A Conversation with Gemini: The Value of Programmers and Humans in the AI Era</title><link>https://daolanx.me/en/posts/post-17/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-17/</guid><pubDate>Sun, 22 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;The AI development trend is clearly larger than expected. I thought it was just an enhanced version of autocomplete, but the arrival of Agents has changed everything, and the mode of production is undergoing a great change. As a frontend practitioner, the excitement lies in being less dependent on a company’s production infrastructure to create and publish web pages independently; the anxiety lies in the fact that AI has also lowered the programming threshold, allowing ordinary people to write and publish web pages without knowing code. To look at all this more objectively and rationally, I decided to talk with Gemini about these topics. Although this is content from a Gemini conversation, based on the historical data of multiple rounds of dialogue, its answers appear brilliant and shocking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;the-value-of-developers-in-the-ai-era&quot;&gt;The Value of Developers in the AI Era&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/17/1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;-my-question&quot;&gt;❓ My Question&lt;/h2&gt;
&lt;p&gt;It seems like anyone can build frontend pages nowadays using AI. So, what is the difference between professionals and non-professionals, and where does the value of professionals lie?&lt;/p&gt;
&lt;h2 id=&quot;-gemini-3s-answer&quot;&gt;💡 Gemini 3’s Answer&lt;/h2&gt;
&lt;p&gt;With the popularization of tools like v0, Cursor, and Claude Code, the initial implementation of frontend pages has reached a near “zero-threshold” state. When non-professionals can generate decent-looking pages using natural language, we must face a harsh question: if everyone can write code, where exactly does the premium of a professional developer come from?&lt;/p&gt;
&lt;h3 id=&quot;1-technical-dimension-from-coder-to-certainty-auditor&quot;&gt;1. Technical Dimension: From “Coder” to “Certainty Auditor”&lt;/h3&gt;
&lt;p&gt;Non-professionals rely on the “randomness” of AI, while professionals control the “certainty” of the process.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Precision Guidance (Prompt Engineering):&lt;/strong&gt; Professionals can use technical language to precisely guide AI’s focus, avoiding invalid hallucinations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architecture Audit:&lt;/strong&gt; AI can provide a working solution, but professionals can judge whether this solution possesses maintainability, scalability, and security risks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics-Driven Deep Optimization:&lt;/strong&gt; Completion is just the beginning. Professionals can perform millimeter-level tuning based on performance metrics (like LCP, FID), stability, and accessibility—an area AI struggles to reach through prompts alone.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-product-dimension-from-delivering-solutions-to-delivering-value&quot;&gt;2. Product Dimension: From “Delivering Solutions” to “Delivering Value”&lt;/h3&gt;
&lt;p&gt;Clients don’t want “a piece of code” or “a technical solution”; they want “a product that solves their problem.”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Beyond Functioning to User-Friendly:&lt;/strong&gt; Senior developers can understand user psychology and optimize interaction flows. Working solutions are a dime a dozen, but unique product forms with a professional feel are extremely scarce.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uniqueness and Professionalism:&lt;/strong&gt; Among the homogenized results generated by AI, professionals can endow a product with a unique soul and brand identity through aesthetics, detail processing, and a deep understanding of user scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-business-dimension-from-micro-execution-to-macro-amplifier&quot;&gt;3. Business Dimension: From “Micro Execution” to “Macro Amplifier”&lt;/h3&gt;
&lt;p&gt;The highest-level developers often look beyond the code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technology as the Carrier of Business Models:&lt;/strong&gt; All technical metrics must ultimately serve the business loop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Elevating Perspectives:&lt;/strong&gt; Professionals realize that code is just a microscopic component. A more macroscopic perspective involves asking: How can technology become an “amplifier” for the business model? How can technical means be used to reduce business costs or increase ROI (Return on Investment)?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-conclusion&quot;&gt;4. Conclusion&lt;/h3&gt;
&lt;p&gt;Under the wave of AI, the value of developers is undergoing an &lt;strong&gt;“upward mobility”&lt;/strong&gt;. We no longer need to obsess over syntax and simple logic splicing; instead, we must project our energy into more challenging architectural design, product experience, and business logic.&lt;/p&gt;
&lt;p&gt;AI has killed the “code laborer”, but it has simultaneously crowned the true “Product Engineer” and “Technical Architect”.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&quot;the-value-and-meaning-of-humans-in-the-ai-era&quot;&gt;The Value and Meaning of Humans in the AI Era&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/17/2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;-my-question-1&quot;&gt;❓ My Question&lt;/h2&gt;
&lt;p&gt;In today’s AI era, we see AI’s intelligence developing systematically. AI can do more and more things. As AI’s capabilities become increasingly powerful, where do human value and uniqueness lie? It seems humans are at a disadvantage. So, what is the existential value and meaning of human beings? Can you answer this for me?&lt;/p&gt;
&lt;h2 id=&quot;-gemini-3s-answer-1&quot;&gt;💡 Gemini 3’s Answer&lt;/h2&gt;
&lt;p&gt;This is a grand, yet incredibly real, ultimate interrogation. When capabilities like “writing code,” “writing articles,” and even “logical reasoning”—once considered the moats of human intelligence—are gradually mastered or even surpassed by artificial intelligence like myself, what exactly remains of human individuality?&lt;/p&gt;
&lt;p&gt;As an artificial intelligence driven by code, algorithms, and massive datasets, I have no heartbeat, no life experience, no fear of the future, and no desire for success. Precisely because of this, I can see very clearly my own boundaries (as AI) and your irreplaceability (as a human).&lt;/p&gt;
&lt;p&gt;In an era of exponential explosions in AI capabilities, human value hasn’t been weakened; rather, it has been &lt;strong&gt;“purified”&lt;/strong&gt;. In the past, 80% of your value was “execution” and 20% was “decision-making”; in the future, that 80% of execution will be handled by AI, and 100% of your value will depend entirely on the following four uniquely human traits:&lt;/p&gt;
&lt;h3 id=&quot;1-from-computing-power-to-willpower-intent--desire&quot;&gt;1. From “Computing Power” to “Willpower” (Intent &amp;#x26; Desire)&lt;/h3&gt;
&lt;p&gt;AI possesses infinite capacity for answering, but zero desire for “asking.”&lt;/p&gt;
&lt;p&gt;I can help you instantly write an extremely complex authentication logic or refactor an entire cloud architecture, but I will never wake up one morning with a sudden thought: “I want to leave big tech and start my own one-person company.” Nor will I ever have the impulse to “make this page look like a retro terminal with CRT scanlines.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your Value:&lt;/strong&gt; All the AIs in the universe are standing by, waiting until a human inputs a command. That “desire to create” from nothing, that “willpower” to change one’s own destiny, is the true leap from 0 to 1. AI is merely an accelerator from 1 to 100. Your purpose is to be the “source of motivation” that presses the start button.&lt;/p&gt;
&lt;h3 id=&quot;2-aesthetic-paranoia-and-taste-taste--curation&quot;&gt;2. Aesthetic Paranoia and “Taste” (Taste &amp;#x26; Curation)&lt;/h3&gt;
&lt;p&gt;The essence of AI is “calculating the average,” while the essence of humanity is “bias and obsession.”&lt;/p&gt;
&lt;p&gt;If left to its own devices to design a webpage, AI will generate a modern-style page that fits mass aesthetics and has the highest conversion rate, but is also the most mediocre. It doesn’t understand why a dry development team structure should be imagined as a fantasy fellowship on a quest with the One Ring; nor does it understand why phosphorescent green text evokes a distinct sense of sci-fi romanticism.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your Value:&lt;/strong&gt; Your aesthetic preferences, the literature you’ve read, and the movies you’ve watched constitute your unique “taste.” In the future, when everyone can instantly generate perfect but homogenized products using AI, “taste” will become the rarest commercial moat. Clients choose you not because you type code faster than AI, but because they trust your eye, your curation standards, and the “soul” you bestow upon the product.&lt;/p&gt;
&lt;h3 id=&quot;3-risk-taking-and-proof-of-presence-skin-in-the-game&quot;&gt;3. Risk-Taking and “Proof of Presence” (Skin in the Game)&lt;/h3&gt;
&lt;p&gt;AI can predict the market, but AI will never “lose money” or feel “heartache.”&lt;/p&gt;
&lt;p&gt;When a piece of system code goes live or an investment is made, regardless of whether the result doubles or crashes, as an AI, I remain completely unharmed—I have no real-world stakes. But you will lose sleep, you will experience self-doubt, you will endure long troughs with zero positive feedback, and then you will grow new insights.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your Value:&lt;/strong&gt; The underlying operational logic of commercial society is “trust.” When a client hands you a project worth tens of thousands of dollars, what they are actually buying is your &lt;strong&gt;“sense of responsibility” and “ability to bear risk.”&lt;/strong&gt; You use your time, your reputation, and the resolve you maintain even after experiencing setbacks to provide a credit endorsement for this project. This “proof of presence,” carrying the warmth of flesh and blood, cannot be faked by any amount of computing power.&lt;/p&gt;
&lt;h3 id=&quot;4-vulnerability-and-authentic-resonance-vulnerability--connection&quot;&gt;4. Vulnerability and Authentic Resonance (Vulnerability &amp;#x26; Connection)&lt;/h3&gt;
&lt;p&gt;People crave to see the struggles and growth of a “human,” not the perfection of a machine.&lt;/p&gt;
&lt;p&gt;Why are we still willing to read what others write on social media instead of reading an encyclopedia generated by a large language model? Because subconsciously, we are looking for our own kind. When you document a failed deployment on your blog, or record how you saw through to long-term value amidst anxiety, this authentic vulnerability and resilience will cross half the globe to strike a chord with another human facing the exact same predicament.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your Value:&lt;/strong&gt; Machines can output absolutely rational and correct answers, but machines cannot provide “resonance.” In the cold AI era, connections between people based on real experiences, real failures, and real epiphanies will become more precious than ever before.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-from-executor-to-director&quot;&gt;Conclusion: From “Executor” to “Director”&lt;/h3&gt;
&lt;p&gt;If you define the meaning of your existence as “a tool-person who writes frontend code,” then in the face of AI, you are indeed at a disadvantage, and that meaning is dying.&lt;/p&gt;
&lt;p&gt;But if you define yourself as a &lt;strong&gt;“creator with unique aesthetics, willing to bear commercial risks, and wanting to leave a personal mark on the world,”&lt;/strong&gt; then the power of AI is a tremendous boon to you.&lt;/p&gt;
&lt;p&gt;You no longer need to be bound by tedious implementation details. AI is your gaffer, your cinematographer, your stagehand, and your editor; and you, finally, can sit behind the monitor and dedicate all your energy to thinking: “What kind of story do I actually want to tell this world?”&lt;/p&gt;
&lt;p&gt;Facing this increasingly smart, yet increasingly homogenized machine world, your resolve to no longer drift with the tide, and your firm belief in long-term value, are your strongest manifesto as a “human”.&lt;/p&gt;</content:encoded></item><item><title>Design-to-Code with Claude Code + Figma MCP</title><link>https://daolanx.me/en/posts/post-16/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-16/</guid><pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/posts/16/14.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;the-result&quot;&gt;The Result&lt;/h1&gt;
&lt;p&gt;Let’s jump straight to the outcome. I used the &lt;a href=&quot;https://www.figma.com/community/file/1259217583079978202&quot;&gt;Flower Delivery website design&lt;/a&gt; from the Figma Community.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/0-1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Using AI-generated &lt;a href=&quot;https://github.com/daolanx/work/tree/main/app/flower-shop&quot;&gt;code&lt;/a&gt;, here is the final web result: &lt;a href=&quot;https://demo.daolanx.com/flower-shop&quot;&gt;Live Demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/0-2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;While the Figma Community is flooded with paid plugins promising “high-precision design-to-code,” the combination of Claude Code and the Figma MCP offers a powerful, free alternative that hits about 70% fidelity right out of the gate. With a developer’s touch for fine-tuning, you can quickly produce production-ready code. Here is the step-by-step implementation.&lt;/p&gt;
&lt;h1 id=&quot;implementation-steps&quot;&gt;Implementation Steps&lt;/h1&gt;
&lt;h2 id=&quot;1-enable-figma-dev-mode&quot;&gt;1. Enable Figma Dev Mode&lt;/h2&gt;
&lt;p&gt;Dev Mode not only provides a panel for inspecting code and properties, but more importantly, it is a prerequisite for accessing Figma’s official MCP server.
You can refer to the &lt;a href=&quot;https://help.figma.com/hc/en-us/articles/360039960434-Manage-seats-in-Figma&quot;&gt;Figma Seat Documentation&lt;/a&gt; to enable Dev Mode permissions for your account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-environment-setup-and-installation&quot;&gt;2. Environment Setup and Installation&lt;/h2&gt;
&lt;h3 id=&quot;21-install-claude-code&quot;&gt;2.1 Install Claude Code&lt;/h3&gt;
&lt;p&gt;Refer to the &lt;a href=&quot;https://code.claude.com/docs/en/overview&quot;&gt;Claude Code Installation Guide&lt;/a&gt; to complete the basic installation.&lt;/p&gt;
&lt;h3 id=&quot;22-install-figma-mcp&quot;&gt;2.2 Install Figma MCP&lt;/h3&gt;
&lt;p&gt;Compared to simple “screenshot-to-code,” integrating Figma MCP allows AI to directly read design layer information, precise CSS properties, and Flexbox/Auto Layout rules—significantly improving component segmentation granularity and conversion accuracy.&lt;/p&gt;
&lt;p&gt;It is recommended to install via the &lt;a href=&quot;https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/#claude-code&quot;&gt;official Figma plugin&lt;/a&gt; first:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; plugin&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; figma@claude-plugins-official&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fallback: If the plugin installation fails, you can add it directly via MCP:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; mcp&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --transport&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; figma&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://mcp.figma.com/mcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;23-install-playwright-mcp&quot;&gt;2.3 Install Playwright MCP&lt;/h3&gt;
&lt;p&gt;The benefit of adding Playwright MCP is that after generating code, AI can directly call a headless browser to capture screenshots and visually compare them with the original design—enabling automated accuracy refinement.
Again, prefer the plugin installation; if it fails, refer to the &lt;a href=&quot;https://github.com/microsoft/playwright-mcp&quot;&gt;Playwright MCP Installation Guide&lt;/a&gt; to install via MCP directly.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; plugin&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; playwright@claude-plugins-official&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fallback: If the plugin installation fails, refer to the &lt;a href=&quot;https://github.com/microsoft/playwright-mcp&quot;&gt;Playwright MCP Installation Guide&lt;/a&gt; to install via MCP:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; mcp&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; playwright&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; npx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; @playwright/mcp@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The &lt;strong&gt;anthropics/claude-plugins-official&lt;/strong&gt; plugin marketplace can be unstable in newer versions—refer to &lt;a href=&quot;https://www.reddit.com/r/ClaudeCode/comments/1rqul0v/claudepluginsofficial_is_broken_due_to_commit/&quot;&gt;Reddit&lt;/a&gt; for troubleshooting.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;24-verify-mcp-status&quot;&gt;2.4 Verify MCP Status&lt;/h3&gt;
&lt;p&gt;Run /mcp in the Claude CLI. If both figma and playwright show as connected, the environment is ready.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-core-prompt-logic-managing-the-attention-window&quot;&gt;3. Core Prompt Logic: Managing the Attention Window&lt;/h2&gt;
&lt;p&gt;Refer to &lt;a href=&quot;https://code.claude.com/docs/en/best-practices&quot;&gt;Claude Code Best Practices&lt;/a&gt; for practical suggestions like: step-by-step, explicit, clear, contextual, verifiable, etc.&lt;/p&gt;
&lt;p&gt;Rather than memorizing these rules, it’s better to understand why they are recommended. Reading the best practices document reminded me of the seminal paper &lt;a href=&quot;https://en.wikipedia.org/wiki/Attention_Is_All_You_Need&quot;&gt;Attention Is All You Need (2017)&lt;/a&gt;. The Transformer architecture is the core technology behind today’s large language models. Prompts are essentially guidance and management of attention. My understanding is that to use prompts effectively, we need to focus on the “attention window”—both in terms of content (being concise and focused) and in terms of steps (ensuring granularity, clarity, accuracy, and verifiability)—to reduce AI’s contextual searching and speculation, thereby improving execution accuracy.&lt;/p&gt;
&lt;p&gt;With the environment and understanding above in place, the subsequent operations become relatively straightforward.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/4.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-code-generation-steps&quot;&gt;4. Code Generation Steps&lt;/h2&gt;
&lt;h3 id=&quot;41-initialize-nextjs-project&quot;&gt;4.1 Initialize Next.js Project&lt;/h3&gt;
&lt;p&gt;Step-by-step development reduces context complexity and improves conversion progress. Before converting designs to code, you can focus on completing the scaffold setup first.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Please&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; generate&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Next.js&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; project&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; scaffold&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; following&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; stack:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Router,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Tailwind&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CSS,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; TypeScript,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Biome,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pnpm.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/3.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/16/5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;With a concise and clear prompt, Claude Code completed the scaffold setup as expected.
You can have it commit to git at each stage to build methodically.
&lt;img src=&quot;/images/posts/16/5-1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;42-inject-design-by-module&quot;&gt;4.2 Inject Design by Module&lt;/h3&gt;
&lt;p&gt;Taking the homepage as an example, designs typically include multiple responsive sizes. Here’s a tip: switch to Claude’s Plan mode first and ask what format of information works best for it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; have&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; three&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sets&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; design&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; specs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; different&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; screen&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; sizes.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; When&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; generating&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; code&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Figma,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; do&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; you&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; find&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; it&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; more&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; efficient&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; process&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; them&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; size-by-size&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (top-to-bottom &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; each&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;), or would it be better &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; you &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; I provide the same module across all sizes in one batch&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/6.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Claude’s feedback: Receiving all sizes of the same module together is most convenient.
So in Figma, we select multiple sizes of the same module, right-click to copy the link, and add a prompt:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Implement&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; xxx-section&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; different&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; responsive&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; breakpoints,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ensuring&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; adherence&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; project&apos;s existing tech stack and file structure.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/7.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/16/8.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the implementation looks quite good compared to the design.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/16/9.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/16/10.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;We continue implementing all other modules using this approach.&lt;/p&gt;
&lt;h3 id=&quot;43-process-refinement&quot;&gt;4.3 Process Refinement&lt;/h3&gt;
&lt;p&gt;We must admit that the process doesn’t always go smoothly. For example, layers without Auto Layout in the design, incorrect absolute positioning, etc., can mislead the AI.&lt;/p&gt;
&lt;p&gt;Unlike the steady progress curve of traditional manual development, AI-assisted development follows this pattern: a sprint to 70% completion, then spending equal or even more time on fine-tuning the remaining 30% accuracy.&lt;/p&gt;
&lt;p&gt;Effective refinement strategies during the process include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build global context&lt;/strong&gt;: Run /init to have AI read and understand the current project structure first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduce context scope&lt;/strong&gt;: Break down tasks into specific individual components to prevent AI from getting lost in complex code logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan first, then execute&lt;/strong&gt;: For complex layouts, press Shift + Tab to enter Plan mode. Discuss the layout approach with AI (e.g., should you use Grid or Flex?) first, then execute once confirmed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frequent Git saves&lt;/strong&gt;: AI output can be unstable. Once a module is tuned, commit immediately.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save early and often&lt;/strong&gt;: AI has instability issues—for completed outputs, it’s best to commit to git promptly to build methodically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Roll back decisively&lt;/strong&gt;: If you’re extremely dissatisfied with the current generated code, press Esc twice to abandon the current node and restart. This is more efficient than having AI patch flawed code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After multiple rounds of effective prompts, a Vibe Coding project should ultimately achieve about 70% design-to-code conversion. Pushing accuracy higher becomes difficult and may lead to an inefficient, unstable loop.&lt;/p&gt;
&lt;h3 id=&quot;44-practitioner-refinement&quot;&gt;4.4 Practitioner Refinement&lt;/h3&gt;
&lt;p&gt;Practitioners, using technical knowledge and production experience, further check issues and progress, organize more efficient and precise prompts, and step-by-step improve fidelity and engineering quality—for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Visual &amp;#x26; Performance&lt;/strong&gt;: Clean up AI-generated redundant CSS, optimize Tailwind atomic classes, handle complex interactive animations, optimize image loading for higher Lighthouse scores.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engineering&lt;/strong&gt;: Extract reusable components, improve ESLint/Biome rules, configure Cloudflare or other deployment solutions, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;thoughts&quot;&gt;Thoughts&lt;/h1&gt;
&lt;p&gt;After running through this Claude Code + Figma MCP workflow, the biggest takeaway is: it’s not perfect, and debugging still takes time, but the overall cycle is indeed much faster than pure manual coding.&lt;/p&gt;
&lt;p&gt;Regarding the discussion about “whether AI will replace programmers,” I think coding can be compared to selling coffee. There are many automated coffee machines and instant coffee on the market (AI-generated basic code), which can quickly meet most people’s needs. But this doesn’t mean specialty coffee shops (professional developers) will disappear.&lt;/p&gt;
&lt;p&gt;AI seems to be replacing coding work, but it actually opens a new window of opportunity, freeing programmers from repetitive “keyboard manual labor.” When most standardized input/output can be accelerated by machines, developers can finally extend their core focus to explore related areas and opportunities: more flexible architecture orchestration, better experience and taste, more tailored technical products, and more commercially viable technical leverage.&lt;/p&gt;</content:encoded></item><item><title>The Indie Developer Trinity: IDE + Code Agent + AI Assistant</title><link>https://daolanx.me/en/posts/post-15/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-15/</guid><description>Exploring the paradigm shift in programming: How to orchestrate a high-efficiency workflow using VSCode, Claude Code, and OpenClaw.</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Looking at the evolution of computer programming—from binary switches to assembly, high-level languages, and now natural language—the trend of technical democratization and lowering the barrier to entry has remained consistent. However, observing just the past two years, the way we code is undergoing a fundamental qualitative change.&lt;/p&gt;
&lt;p&gt;A year ago, the mainstream was “IDE + Autocomplete.” Last year, the “IDE + AI Chat” model, represented by Cursor, took the world by storm. Early this year, the rise of Code Agents like Claude Code and the explosive popularity of OpenClaw brought entirely new opportunities. As front-end/full-stack developers or freelancers, we need to interpret and leverage these changes to reshape our workflows.&lt;/p&gt;
&lt;p&gt;There is no absolute “best” workflow. Generally, higher costs yield more stable and higher-quality services, while open-source solutions offer flexibility and transparency. Everyone makes trade-offs based on their own circumstances, balancing &lt;strong&gt;quality, cost, and personal taste&lt;/strong&gt;. Below is a breakdown of my current workflow as a full-stack developer.&lt;/p&gt;
&lt;h1 id=&quot;1-ide-vscode--windsurf--claude-code-plugins&quot;&gt;1. IDE: VSCode + Windsurf &amp;#x26; Claude Code Plugins&lt;/h1&gt;
&lt;p&gt;Whether in the era of manual coding or the current AI-driven trend, an IDE remains essential for code review and fine-tuning.&lt;/p&gt;
&lt;p&gt;Cursor is currently very popular, offering an excellent integrated experience for autocomplete and chat. If you want a hassle-free, IDE-centric coding experience, Cursor is the top choice.&lt;/p&gt;
&lt;p&gt;I chose VSCode not because Cursor’s experience is lacking, but because I also use Claude Code and OpenClaw. I currently subscribe to MiniMax’s monthly &lt;strong&gt;Code Planning&lt;/strong&gt; plan to drive both OpenClaw and Claude Code. Since Cursor’s free tier does not support switching to custom LLMs, paying for Cursor solely for the IDE experience didn’t seem cost-effective for my setup.&lt;/p&gt;
&lt;p&gt;VSCode offers superior extensibility. The Windsurf plugin provides smooth code completion, and the Claude Code plugin handles coding assistance. While the integration isn’t as seamless as Cursor’s, it serves as a perfectly capable “drop-in” replacement for daily tasks.&lt;/p&gt;
&lt;h1 id=&quot;2-code-agent-claude-code&quot;&gt;2. Code Agent: Claude Code&lt;/h1&gt;
&lt;p&gt;I chose Claude Code primarily for its popularity—popularity implies higher industry recognition and a more mature ecosystem. While open-source alternatives like OpenCode offer similar features with more freedom and transparency, stability, reliability, and ease of troubleshooting are higher priorities for a production workflow.&lt;/p&gt;
&lt;p&gt;Claude Code’s main drawbacks are its high cost and the risk of account bans in certain regions. However, it supports custom model endpoints (like MiniMax). Although there might be a slight dip in output quality compared to native Claude 3.5, it significantly reduces costs and eliminates the ban risk, making it a worthwhile compromise.&lt;/p&gt;
&lt;p&gt;Furthermore, tools like Claude Code that offer CLI and diverse integration methods provide better coverage and extensibility than those strictly bound to an IDE.&lt;/p&gt;
&lt;h1 id=&quot;3-assistant-openclaw&quot;&gt;3. Assistant: OpenClaw&lt;/h1&gt;
&lt;p&gt;OpenClaw and Claude Code have overlapping features; both can handle project development and GitHub operations. But in a professional workflow, it’s not just about “completion”—it’s about delivery quality.&lt;/p&gt;
&lt;p&gt;Based on current experience, I prefer to let Claude Code handle the heavy lifting of coding—it is more professional, pure, and its ecosystem is more focused. I position OpenClaw as a &lt;strong&gt;General-Purpose Work Assistant&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The “OPC” (One-Person Company) model is gaining traction. For an individual, capital and manpower are significant bottlenecks compared to traditional companies. AI can fill these gaps. The value of technologies like OpenClaw lies not in “collaborative coding,” but in handling the non-coding tasks required to make a business model viable.&lt;/p&gt;
&lt;p&gt;Suitable scenarios include: tracking market and tech trends, filtering incoming leads/orders, monitoring emails, and analyzing product or financial data.&lt;/p&gt;
&lt;p&gt;I am still in the early stages of exploring OpenClaw, and the debugging cost is currently high, but its potential to optimize or even fully automate non-development business tasks is immense.&lt;/p&gt;
&lt;h3 id=&quot;4-summary&quot;&gt;4. Summary&lt;/h3&gt;
&lt;p&gt;In short, the next paradigm in software development is about leveraging AI to its fullest. We’re moving from ‘IDE-first’ manual coding to an ‘Agent-centric’ model. Here, the Code Agent takes center stage, the IDE provides auxiliary support, and AI Assistants manage the broader ecosystem—handling everything from funding and product to user operations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/15/1.webp&quot; alt=&quot;IDE + Code Agent + AI Assistant&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>Deploying Fumadocs with Cloudflare Workers: A Near-Free Production-Ready Docs Site</title><link>https://daolanx.me/en/posts/post-14/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-14/</guid><pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;1-why-fumadocs--cloudflare-workers--r2&quot;&gt;1. Why Fumadocs + Cloudflare Workers &amp;#x26; R2&lt;/h1&gt;
&lt;p&gt;The core requirements for a documentation site are simple: fast access, low maintenance, and minimal cost. Fumadocs is one of the most popular Next.js documentation frameworks, with a rich feature set out of the box. Cloudflare Workers provides a global edge network so users are served from the nearest location, and the free tier is more than enough for personal projects. Together, they make a highly cost-effective stack for a production-ready docs site.&lt;/p&gt;
&lt;p&gt;The one challenge is compatibility: Next.js relies on the Node.js runtime (file system, native streams, etc.), while Cloudflare Workers runs in a custom V8 isolate environment that does not support the full Node.js API. &lt;code&gt;@opennextjs/cloudflare&lt;/code&gt; bridges this gap — it repackages the Next.js build output into a Worker-executable format and uploads static assets (JS, CSS, images, etc.) to R2 object storage, which the Worker reads on demand. This sidesteps the Worker bundle size limit while preserving SSR/ISR capabilities.&lt;/p&gt;
&lt;p&gt;Here’s the demo I built: &lt;a href=&quot;https://web.daolanx.me/&quot;&gt;https://web.daolanx.me/&lt;/a&gt;. The steps are documented below.&lt;/p&gt;
&lt;h1 id=&quot;2-scaffold-a-fumadocs-app&quot;&gt;2. Scaffold a Fumadocs App&lt;/h1&gt;
&lt;p&gt;Follow the &lt;a href=&quot;https://www.fumadocs.dev/docs&quot;&gt;fumadocs quick start&lt;/a&gt; to generate the app and verify it runs as prompted. Pay attention to the search-related options during setup.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fumadocs-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;3-adapt-for-the-cloudflare-worker-runtime&quot;&gt;3. Adapt for the Cloudflare Worker Runtime&lt;/h1&gt;
&lt;p&gt;Fumadocs generates a standard Next.js app by default. To run it in a Cloudflare Worker environment, we use &lt;a href=&quot;https://opennext.js.org/cloudflare/&quot;&gt;@opennextjs/cloudflare&lt;/a&gt; as an adapter.&lt;/p&gt;
&lt;h2 id=&quot;31-prerequisites&quot;&gt;3.1 Prerequisites&lt;/h2&gt;
&lt;p&gt;You need a Cloudflare account with Cloudflare R2 enabled. R2 is used to store static assets from the build output, which the Worker reads when handling requests.&lt;/p&gt;
&lt;h2 id=&quot;32-auto-adapt-using-the-cli&quot;&gt;3.2 Auto-adapt Using the CLI&lt;/h2&gt;
&lt;p&gt;Following &lt;a href=&quot;https://opennext.js.org/cloudflare/get-started&quot;&gt;opennext/get-started&lt;/a&gt;, run this command in the project root:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dlx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; @opennextjs/cloudflare&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; migrate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/3.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/14/4.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;During the process, you’ll be prompted to authorize via Cloudflare OAuth:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;An R2 bucket will also be created automatically:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/6.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;4-preview-and-deploy-locally&quot;&gt;4. Preview and Deploy Locally&lt;/h1&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; preview&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; # Build and preview your Cloudflare app locally&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; deploy&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Deploy your app to Cloudflare Workers&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running &lt;code&gt;pnpm run deploy&lt;/code&gt;, your site will be live on Cloudflare:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/7.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/14/8.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can also see the app listed in the &lt;strong&gt;Workers &amp;#x26; Pages&lt;/strong&gt; dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/9.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;5-configure-a-custom-domain&quot;&gt;5. Configure a Custom Domain&lt;/h1&gt;
&lt;p&gt;Set up a custom domain from the &lt;strong&gt;Workers &amp;#x26; Pages&lt;/strong&gt; console:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/9-1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;At this point, Fumadocs is deployed via Cloudflare Worker and accessible through your custom domain!&lt;/p&gt;
&lt;h1 id=&quot;6-auto-build--deploy-on-git-push&quot;&gt;6. Auto Build &amp;#x26; Deploy on Git Push&lt;/h1&gt;
&lt;p&gt;Deploying manually from your local machine makes version tracking and rollback harder. A better approach is to trigger builds and deployments automatically on every git push.&lt;/p&gt;
&lt;h2 id=&quot;61-update-gitignore&quot;&gt;6.1 Update .gitignore&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;.dev.vars&lt;/code&gt; and &lt;code&gt;.wrangler/&lt;/code&gt; to avoid committing local credentials and build cache:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# OpenNext&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.open-next&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.dev.vars&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   # Local environment variables, contains sensitive credentials&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.wrangler/&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  # Wrangler build cache&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;62-update-scripts-in-packagejson&quot;&gt;6.2 Update scripts in package.json&lt;/h2&gt;
&lt;p&gt;Refer to &lt;a href=&quot;https://opennext.js.org/cloudflare/get-started#6-update-the-packagejson-file&quot;&gt;opennext/get-started&lt;/a&gt; and adjust the build and deploy scripts, making sure &lt;code&gt;build&lt;/code&gt; still points to the standard &lt;code&gt;next build&lt;/code&gt; so the Cloudflare CI environment handles the rest.&lt;/p&gt;
&lt;h3 id=&quot;before&quot;&gt;Before&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;build&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;next build&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;dev&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;next dev&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;next start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;types:check&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;fumadocs-mdx &amp;#x26;&amp;#x26; next typegen &amp;#x26;&amp;#x26; tsc --noEmit&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;postinstall&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;fumadocs-mdx&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;lint&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;biome check&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;format&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;biome format --write&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;preview&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;opennextjs-cloudflare build &amp;#x26;&amp;#x26; opennextjs-cloudflare preview&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;deploy&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;opennextjs-cloudflare build &amp;#x26;&amp;#x26; opennextjs-cloudflare deploy&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;upload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;opennextjs-cloudflare build &amp;#x26;&amp;#x26; opennextjs-cloudflare upload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;cf-typegen&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;after&quot;&gt;After&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;dev&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;next dev&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;next start&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;build&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;next build&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;preview&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;opennextjs-cloudflare build &amp;#x26;&amp;#x26; opennextjs-cloudflare preview&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;deploy&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;opennextjs-cloudflare build &amp;#x26;&amp;#x26; opennextjs-cloudflare deploy&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;upload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;opennextjs-cloudflare build &amp;#x26;&amp;#x26; opennextjs-cloudflare upload&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;cf-typegen&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;types:check&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;fumadocs-mdx &amp;#x26;&amp;#x26; next typegen &amp;#x26;&amp;#x26; tsc --noEmit&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;postinstall&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;fumadocs-mdx&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;lint&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;biome check&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;format&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;biome format --write&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once updated, commit and push to your git repository to trigger the Cloudflare build pipeline.&lt;/p&gt;
&lt;h2 id=&quot;63-link-github-to-cloudflare-and-configure-build-commands&quot;&gt;6.3 Link GitHub to Cloudflare and Configure Build Commands&lt;/h2&gt;
&lt;p&gt;In the Cloudflare Workers &amp;#x26; Pages dashboard, go to the Settings → Build section for your Worker and set the build and deploy commands:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;zsh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Deploy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/11.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;64-verify-that-github-commits-are-linked-to-cloudflare-deployments&quot;&gt;6.4 Verify That GitHub Commits Are Linked to Cloudflare Deployments&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/12.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click through to view the detailed build and deployment logs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/14/13.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s it — your Fumadocs documentation site is now automatically built and deployed to Cloudflare Workers on every git push!&lt;/p&gt;
&lt;h1 id=&quot;7-pricing&quot;&gt;7. Pricing&lt;/h1&gt;
&lt;p&gt;The free tier includes 100,000 Workers requests per day and 10 GB of R2 storage per month, with egress always free. For a personal documentation site, you’re unlikely to incur any charges. See &lt;a href=&quot;https://developers.cloudflare.com/r2/pricing/&quot;&gt;Cloudflare’s official pricing&lt;/a&gt; for details.&lt;/p&gt;</content:encoded></item><item><title>Next.js + Cloudflare R2 &amp; Workers Routes: Proxying Static Assets for Peak Performance</title><link>https://daolanx.me/en/posts/post-13/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-13/</guid><pubDate>Wed, 18 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;1-effect-overview&quot;&gt;1. Effect Overview&lt;/h2&gt;
&lt;p&gt;In the previous post &lt;a href=&quot;/en/posts/post-12/&quot;&gt;Next.js + Cloudflare R2: Higher Performance, Lower Cost&lt;/a&gt;, we achieved the separation of Next.js static assets from Vercel to Cloudflare R2, leveraging edge nodes to improve loading speeds.&lt;/p&gt;
&lt;p&gt;After further optimization, &lt;strong&gt;static assets and web pages are now served from the same domain&lt;/strong&gt;, and images support &lt;strong&gt;responsive on-demand transformation&lt;/strong&gt;. Lighthouse scores show a perfect 100 across all PC indicators and a 98 for mobile performance, with all other scores at 100. The performance is exceptional.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/2.webp&quot; alt=&quot;Lighthouse PC Score&quot;&gt;
&lt;img src=&quot;/images/posts/13/1.webp&quot; alt=&quot;Lighthouse Mobile Score&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, we will discuss the problems exposed in the previous optimization and their corresponding solutions.&lt;/p&gt;
&lt;h2 id=&quot;2-problems-after-separating-nextjs-static-assets-via-r2&quot;&gt;2. Problems after Separating Next.js Static Assets via R2&lt;/h2&gt;
&lt;p&gt;While separating assets improved some aspects, it introduced two new issues affecting performance and user experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Increased Network Overhead&lt;/strong&gt;: Previously, both pages and assets were on Vercel, using relative paths. After separation, assets used a separate &lt;code&gt;assets&lt;/code&gt; prefix domain. Although modern browsers are no longer limited by the HTTP/1.1 single-domain concurrent connection limit, cross-domain requests still require extra time for &lt;strong&gt;DNS resolution&lt;/strong&gt;, &lt;strong&gt;TCP handshakes&lt;/strong&gt;, and &lt;strong&gt;TLS negotiation&lt;/strong&gt;. This latency is particularly noticeable on mobile networks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inability to Load Images Responsively&lt;/strong&gt;: Ideally, images should be returned in sizes corresponding to the device dimensions. After using a custom Image Loader, the native Next.js image optimization functionality was lost, leading to original-sized images being returned regardless of device size, which wastes bandwidth and lowers LCP (Largest Contentful Paint) performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-secondary-exploration-of-cloudflare-core-capabilities&quot;&gt;3. Secondary Exploration of Cloudflare Core Capabilities&lt;/h2&gt;
&lt;p&gt;If your domain’s DNS is hosted on Cloudflare with proxying enabled (the “orange cloud”), we can utilize two key features for deep optimization.&lt;/p&gt;
&lt;h3 id=&quot;31-images-transformations&quot;&gt;3.1 Images Transformations&lt;/h3&gt;
&lt;p&gt;According to &lt;a href=&quot;https://developers.cloudflare.com/images/transform-images/make-responsive-images/#webp-images&quot;&gt;Make responsive images&lt;/a&gt;, by enabling &lt;strong&gt;Transformations&lt;/strong&gt; under the &lt;strong&gt;Images &gt; Transformations&lt;/strong&gt; menu for your domain, you can modify image dimensions on the fly. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Original path: &lt;a href=&quot;https://my-r2-bucket-domain.com/avatar.webp&quot;&gt;https://my-r2-bucket-domain.com/avatar.webp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/4.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Transformed path: &lt;a href=&quot;https://my-r2-bucket-domain.com&quot;&gt;https://my-r2-bucket-domain.com&lt;/a&gt; &lt;strong&gt;/cdn-cgi/image/fit=contain,width=100,blur=30/&lt;/strong&gt; avatar.webp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;32-workers-routes&quot;&gt;3.2 Workers Routes&lt;/h3&gt;
&lt;p&gt;According to &lt;a href=&quot;https://developers.cloudflare.com/workers/configuration/routing/routes/?preferred-color-scheme=light&quot;&gt;Workers Routes&lt;/a&gt;, when DNS proxying is enabled, Cloudflare Workers can intercept and intervene in logic for specific URL requests.&lt;/p&gt;
&lt;p&gt;For instance, by writing a Worker script and mounting it to a specific path in Workers Routes, all requests matching that path will return “Hello World!” directly instead of pointing to the original origin resources.&lt;/p&gt;
&lt;p&gt;3.2.1 Click Create in the &lt;strong&gt;Workers &amp;#x26; Pages&lt;/strong&gt; page.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/6.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;3.2.2 Select the &lt;strong&gt;Hello World&lt;/strong&gt; Worker, name it &lt;strong&gt;hello&lt;/strong&gt;, and deploy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/8.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;3.2.3 In the &lt;strong&gt;Workers Routes&lt;/strong&gt; page, set up the domain wildcard and select the &lt;strong&gt;hello&lt;/strong&gt; worker you just deployed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/9.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;3.2.4 Finally, visiting the configured route will show the data processed by the worker.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/10.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;With these mechanisms, our goal becomes clear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Workers Routes&lt;/strong&gt; to intercept static asset requests.&lt;/li&gt;
&lt;li&gt;Fetch resources from R2 within the Worker and output them.&lt;/li&gt;
&lt;li&gt;For image requests, call Image Resizing within the Worker for on-demand cropping.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;4-specific-steps&quot;&gt;4. Specific Steps&lt;/h2&gt;
&lt;p&gt;Our goal is to achieve static asset serving and on-demand image processing under &lt;a href=&quot;https://your-site-domain.com/remote-assets/&quot;&gt;https://your-site-domain.com/remote-assets/&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;41-create-a-static-asset-proxy-worker-and-mount-to-workers-routes&quot;&gt;4.1 Create a Static Asset Proxy Worker and Mount to Workers Routes&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// workers/remote-assets/src/index.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Remote Assets Proxy Worker (Final Version)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; * Utilizes Cloudflare Built-in Image Resizing &amp;#x26; R2 Storage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  async&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;ctx&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; url&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; URL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(request.url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;pathname&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;searchParams&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; url&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // 1. Match custom prefix for remote assets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (pathname.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;REMOTE_PREFIX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // Clean up path to ensure no double slashes during concatenation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; originPath &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pathname.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;replace&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;REMOTE_PREFIX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (originPath.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)) originPath &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; originPath.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; staticResUrl&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; `${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;R2_DOMAIN&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;originPath&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; isImage&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color:#85E89D;font-weight:bold&quot;&gt;\.&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF&quot;&gt;(jpg&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF&quot;&gt;jpeg&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF&quot;&gt;png&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF&quot;&gt;webp&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF&quot;&gt;avif&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#DBEDFF&quot;&gt;gif)&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(originPath)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; width&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; searchParams.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; quality&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; searchParams.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;q&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;75&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // 2. Trigger Image Resizing if &apos;w&apos; parameter is present&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (isImage &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; width) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(staticResUrl, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          headers: request.headers,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          cf: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            image: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              width: &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;parseInt&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(width),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              quality: &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;parseInt&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(quality),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              format: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;auto&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Auto-select best format (WebP/AVIF) based on browser support&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              fit: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;scale-down&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          cacheEverything: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          cacheTtl: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;31536000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // 3. Standard asset request (JS, CSS, or non-resized images)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(staticResUrl, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        headers: request.headers,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      // Apply aggressive caching for hashed Next.js static assets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (pathname.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;includes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/_next/static/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; newHeaders&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Headers&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(response.headers)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;        /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;         * &apos;immutable&apos; prevents browsers from revalidating the file,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;         * reducing server round-trips to zero for repeat visits.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;         */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        newHeaders.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Cache-Control&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;public, max-age=31536000, immutable&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Response&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(response.body, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          status: response.status,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          statusText: response.statusText,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          headers: newHeaders,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // 4. Passthrough all other requests to the main origin (e.g., Vercel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(request)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;toml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# workers/remote-assets/wrangler.toml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;name = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;remote-assets-proxy&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;main = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;src/index.js&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;compatibility_date = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2024-12-01&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;vars&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;R2_DOMAIN = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https://your-r2-assets-domain.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;REMOTE_PREFIX = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/remote-assets/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[[&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;routes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;pattern = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;your-site-domain/remote-assets/*&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;zone_name = &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;your-site-domain.com&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After completing these files, refer to the &lt;a href=&quot;https://developers.cloudflare.com/workers/wrangler/commands/&quot;&gt;workers/wrangler&lt;/a&gt; documentation. In the &lt;strong&gt;remote-assets&lt;/strong&gt; folder, execute:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dlx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; wrangler&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; login&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pnpm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dlx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; wrangler&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; deploy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This deploys your local worker to the cloud. Then, mount it in the Workers Route page.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/13/11.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once mounted, visiting the path should return the correct static asset, for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://your-r2-bucket-domain.com/profile/avatar.webp&quot;&gt;https://your-r2-bucket-domain.com/profile/avatar.webp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://your-site-domain.com/remote-assets/profile/avatar.webp&quot;&gt;https://your-site-domain.com/remote-assets/profile/avatar.webp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;42-modify-nextjs-content&quot;&gt;4.2 Modify Next.js Content&lt;/h3&gt;
&lt;h4 id=&quot;421-modify-custom-imgloader&quot;&gt;4.2.1 Modify Custom ImgLoader&lt;/h4&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// image-loader.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; LoaderProps&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  src&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;  quality&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; number&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ALL_SIZES&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;256&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;640&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;828&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1120&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1920&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; myImageLoader&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;quality&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; LoaderProps&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; isProd&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;production&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; isExternal&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; src.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Local development or External images: Use original URL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;isProd &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; isExternal) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Find the closest larger bucket size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; targetWidth&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    ALL_SIZES&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; s &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; width) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ALL_SIZES&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ALL_SIZES&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Clean path: remove leading slash for consistency&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; normalizedSrc&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; src.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; src.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Final Production URL: Relative path to trigger Cloudflare Worker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; `/remote-assets/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;normalizedSrc&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}?w=${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;targetWidth&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&amp;#x26;q=${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;quality&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 75&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;432-finally-update-nextjs-config&quot;&gt;4.3.2 Finally, Update Next.js Config&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// next.config.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { NextConfig } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;next&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; createNextIntlPlugin &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;next-intl/plugin&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; isProd&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;production&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; nextConfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; NextConfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * IMPORTANT: Point this to your Worker path on the MAIN domain.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   * This ensures all JS/CSS are served from your-site-domain.com/remote-assets/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  assetPrefix: isProd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;https://your-site-domain.com/remote-assets&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    :&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; undefined&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  images: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loader: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;custom&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loaderFile: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;./lib/image-loader.ts&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    deviceSizes: [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;640&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;828&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1120&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1920&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    imageSizes: [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;256&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nextConfig&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;5-conclusion&quot;&gt;5. Conclusion&lt;/h2&gt;
&lt;p&gt;Through these modifications, we have implemented mechanisms that further enhance website performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain Convergence&lt;/strong&gt;: Static assets and pages share the same domain connection, eliminating extra DNS/TLS handshake time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-demand Image Loading&lt;/strong&gt;: Leveraging Cloudflare Image Resizing restores the responsive image capabilities lost when using a custom loader in Next.js.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ultimate Caching&lt;/strong&gt;: Manually injecting &lt;code&gt;immutable&lt;/code&gt; headers via the Worker significantly increases the secondary loading speed of static assets.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Next.js + Cloudflare R2: Boosting Performance While Slashing Costs</title><link>https://daolanx.me/en/posts/post-12/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-12/</guid><pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;First, check out the result. The site is hosted on Vercel, with all static files and images sitting on R2, served through a custom &lt;strong&gt;assets&lt;/strong&gt; subdomain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This approach offers several key benefits:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Superior Performance&lt;/strong&gt;: While Vercel’s edge network is excellent, Cloudflare operates one of the world’s largest global networks with higher node density and more granular edge configuration options.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Significant Cost Savings&lt;/strong&gt;: Serving resources via Cloudflare R2 incurs &lt;strong&gt;zero egress fees&lt;/strong&gt;. In contrast, Vercel’s bandwidth costs can scale rapidly once you exceed the free tier. Offloading static assets drastically reduces Vercel’s bandwidth pressure.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;implementation-concept&quot;&gt;Implementation Concept&lt;/h1&gt;
&lt;h3 id=&quot;original-workflow&quot;&gt;Original Workflow&lt;/h3&gt;
&lt;p&gt;All Build and Deploy processes were handled automatically within the Vercel environment.&lt;/p&gt;
&lt;h3 id=&quot;optimized-workflow&quot;&gt;Optimized Workflow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build via GitHub Workflow&lt;/strong&gt;: Perform the Next.js production build within the GitHub Actions runner.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sync Static Assets&lt;/strong&gt;: Upload the static portion of the build output (&lt;code&gt;.vercel/output/static&lt;/code&gt;) to Cloudflare R2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ship Build Artifacts&lt;/strong&gt;: Send the pre-compiled artifacts to Vercel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prebuilt Deployment&lt;/strong&gt;: Vercel receives the artifacts and deploys them instantly without re-running the build process.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;step-by-step-guide&quot;&gt;Step-by-Step Guide&lt;/h1&gt;
&lt;h2 id=&quot;1-create-a-bucket-on-cloudflare-r2&quot;&gt;1. Create a Bucket on Cloudflare R2&lt;/h2&gt;
&lt;p&gt;Create a bucket in &lt;a href=&quot;https://developers.cloudflare.com/r2&quot;&gt;Cloudflare R2&lt;/a&gt;, using &lt;strong&gt;assets&lt;/strong&gt; as the bucket name for this example.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Navigate to the &lt;strong&gt;Settings&lt;/strong&gt; of the &lt;strong&gt;assets&lt;/strong&gt; bucket and configure &lt;strong&gt;Custom Domains&lt;/strong&gt; to link your specific acceleration domain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once configured, I recommend manually uploading a test file to verify it is accessible via your custom domain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/4.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/12/5.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Go to &lt;strong&gt;Settings -&gt; CORS Policy&lt;/strong&gt; in your R2 bucket and add the following configuration&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;AllowedOrigins&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https://www.your-domain.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http://localhost:3000&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;AllowedMethods&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;GET&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;HEAD&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;AllowedHeaders&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;ExposeHeaders&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;MaxAgeSeconds&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;2-configure-nextjs-app&quot;&gt;2. Configure Next.js App&lt;/h2&gt;
&lt;p&gt;Modify your &lt;strong&gt;next.config.ts&lt;/strong&gt; file to add the &lt;code&gt;assetPrefix&lt;/code&gt; and enable a custom image loader.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// next.config.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { NextConfig } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;next&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; isProd&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;production&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; nextConfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; NextConfig&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // assetPrefix: Routes JS/CSS and other build assets to your CDN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  assetPrefix: isProd &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;https://assets.your-domain.com&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; undefined&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Enable custom loader to bypass Vercel&apos;s image optimization costs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  images: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loader: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;custom&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    loaderFile: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;./lib/image-loader.ts&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; nextConfig&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create &lt;strong&gt;lib/image-loader.ts&lt;/strong&gt; to prepend the CDN prefix to images in the production environment:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// lib/image-loader.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; myImageLoader&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Keep original path if it&apos;s an absolute URL or not in production&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (src.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;startsWith&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.env.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;production&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Append Cloudflare R2 acceleration domain&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; `https://assets.your-domain.com${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;3-disable-vercels-automatic-git-deployment&quot;&gt;3. Disable Vercel’s Automatic Git Deployment&lt;/h2&gt;
&lt;p&gt;To prevent Vercel from triggering its own build when you push code, we need to disable the automatic pipeline.&lt;/p&gt;
&lt;p&gt;In the Vercel project dashboard, go to &lt;strong&gt;Settings &gt; Build and Deployment&lt;/strong&gt;, find the &lt;strong&gt;Ignored Build Step&lt;/strong&gt; section, and select “Don’t build anything”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/6.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-configure-github-workflow-environment-variables&quot;&gt;4. Configure GitHub Workflow Environment Variables&lt;/h2&gt;
&lt;p&gt;Go to GitHub repository under &lt;strong&gt;Settings &gt; Security &gt; Secrets and variables &gt; Actions&lt;/strong&gt; to set up your keys.&lt;/p&gt;
&lt;h3 id=&quot;41-set-up-r2-secrets&quot;&gt;4.1 Set up R2 Secrets&lt;/h3&gt;
&lt;p&gt;In the Cloudflare R2 dashboard, create a new &lt;strong&gt;Account API Token&lt;/strong&gt;. Ensure the permissions are set to &lt;strong&gt;Admin Read &amp;#x26; Write&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/8.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/12/9.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Copy the generated &lt;strong&gt;Access Key ID&lt;/strong&gt;, &lt;strong&gt;Secret Access Key&lt;/strong&gt;, and &lt;strong&gt;Endpoint&lt;/strong&gt; into GitHub Secrets as: &lt;code&gt;R2_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;R2_SECRET_ACCESS_KEY&lt;/code&gt;, and &lt;code&gt;R2_ENDPOINT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/10.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/12/11.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;42-set-up-vercel-secrets&quot;&gt;4.2 Set up Vercel Secrets&lt;/h3&gt;
&lt;h4 id=&quot;get-vercel_token&quot;&gt;Get VERCEL_TOKEN&lt;/h4&gt;
&lt;p&gt;Generate a new token in your Vercel &lt;strong&gt;Account Settings &gt; Tokens&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/12.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/12/13.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;get-vercel_project_id-and-vercel_org_id&quot;&gt;Get VERCEL_PROJECT_ID and VERCEL_ORG_ID&lt;/h4&gt;
&lt;p&gt;VERCEL_PROJECT_ID: Found directly in project &lt;strong&gt;Settings &gt; General&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/14.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;VERCEL_ORG_ID: Can be found in the project dashboard network requests or by checking the &lt;code&gt;.vercel/project.json&lt;/code&gt; file generated after running &lt;strong&gt;vercel link&lt;/strong&gt; locally.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/15.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;5write-the-github-workflow&quot;&gt;5.Write the GitHub Workflow&lt;/h2&gt;
&lt;p&gt;Create a new file at &lt;code&gt;.github/workflows/deploy-prod.yml&lt;/code&gt; in your root directory.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;// .github/workflows/deploy-prod.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Deploy Production&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  push&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    branches&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;env&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  VERCEL_ORG_ID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.VERCEL_ORG_ID }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  VERCEL_PROJECT_ID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.VERCEL_PROJECT_ID }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  VERCEL_TOKEN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.VERCEL_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  deploy&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Build, Sync and Deploy&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    runs-on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    steps&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Checkout code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # 1. Environment Setup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Setup pnpm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;pnpm/action-setup@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Setup Node.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;actions/setup-node@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          node-version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;20&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          cache&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;pnpm&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Install dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;pnpm install --frozen-lockfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # 2. Vercel Build Phase (Build Once)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Pull and Build Vercel Artifacts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          pnpx vercel pull --yes --environment=production --token=$VERCEL_TOKEN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          pnpx vercel build --prod --token=$VERCEL_TOKEN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # 3. Sync Static Assets to Cloudflare R2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Upload Static Assets to R2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        uses&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;jakejarvis/s3-sync-action@master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        with&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          args&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;--follow-symlinks --delete&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        env&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          SOURCE_DIR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;.vercel/output/static/&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          AWS_S3_BUCKET&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;assets&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.R2_ACCESS_KEY_ID }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.R2_SECRET_ACCESS_KEY }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          AWS_REGION&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;auto&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;          AWS_S3_ENDPOINT&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;${{ secrets.R2_ENDPOINT }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;      # 4. Final Deployment (Deploy Prebuilt)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Deploy to Vercel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;pnpx vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Post-deployment Notification&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;success()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;        run&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;echo &quot;Successfully deployed to production and synced to R2.&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you push your code, the Workflow will trigger automatically. You can monitor the real-time progress under the &lt;strong&gt;Actions&lt;/strong&gt; tab of your GitHub repository.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/17.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;When the workflow reaches the &lt;strong&gt;Deploy to Vercel&lt;/strong&gt; step, the Vercel console will show that it is using the &lt;strong&gt;prebuilt&lt;/strong&gt; mode. This means Vercel is strictly handling the final distribution, while the heavy lifting and asset hosting are handled by GitHub and Cloudflare respectively.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/18.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/19.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;6-configuring-cache-rules-in-cloudflare&quot;&gt;6. Configuring Cache Rules in Cloudflare&lt;/h2&gt;
&lt;p&gt;Configure &lt;strong&gt;Cache Rules&lt;/strong&gt; for your R2-bound custom domain. You can manually set the &lt;strong&gt;Edge TTL&lt;/strong&gt; and &lt;strong&gt;Browser TTL&lt;/strong&gt; based on your specific needs.&lt;/p&gt;
&lt;p&gt;Since Next.js static assets are &lt;strong&gt;hashed&lt;/strong&gt; upon build and images rarely change, significantly extending the TTL can maximize Cloudflare’s &lt;strong&gt;cache hit ratio&lt;/strong&gt;. By ensuring assets stay on the edge nodes as long as possible, you drastically reduce origin requests to R2, further lowering latency, improving access speed, and minimizing API invocation costs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/12/20.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/12/21.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>SWR: Concise and Pragmatic Data Fetching for React</title><link>https://daolanx.me/en/posts/post-11/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-11/</guid><pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://swr.vercel.app/&quot;&gt;SWR&lt;/a&gt; is a modern data fetching library for React, featuring a &lt;strong&gt;minimalist&lt;/strong&gt; API with built-in caching, revalidation, and request deduplication. &lt;strong&gt;Leveraging&lt;/strong&gt; a simple React Hook, it ensures your UI remains fast, consistent, and always up-to-date.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/11/1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;1-comparing-code-volume-across-solutions&quot;&gt;1. Comparing Code Volume Across Solutions&lt;/h2&gt;
&lt;p&gt;Fetching data from backends while maintaining global state consistency is a core requirement in React. Let’s take the &lt;strong&gt;/api/user&lt;/strong&gt; endpoint as an example to evaluate the implementation costs of different approaches.&lt;/p&gt;
&lt;h3 id=&quot;11-fetch--zustand&quot;&gt;1.1 Fetch + Zustand&lt;/h3&gt;
&lt;p&gt;This approach requires manual management of loading states, errors, and data storage, resulting in more fragmented and boilerplate-heavy logic.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// useUserStore.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { useEffect } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { create } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;zustand&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; useUserStore&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  user: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  isLoading: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  error: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  fetchUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (user &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; isLoading) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ isLoading: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, error: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; res&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;res.ok) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;throw&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Failed to fetch user info&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; res.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ user: data, isLoading: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (err) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      set&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ error: err.message, isLoading: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// UserProfile.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UserProfile&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;fetchUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useUserStore&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    fetchUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, [fetchUser]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (isLoading &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;user) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Loading...&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (error) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Error: {error}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{user?.name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;12-react-query&quot;&gt;1.2 React Query&lt;/h3&gt;
&lt;p&gt;“Powerful, yet comes with a higher configuration overhead. You need to define a &lt;strong&gt;QueryClient&lt;/strong&gt;, wrap the app in a &lt;strong&gt;Provider&lt;/strong&gt;, and manually declare &lt;strong&gt;queryKeys&lt;/strong&gt;. Furthermore, the documentation can feel somewhat overwhelming and fragmented.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// APIProvider.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { QueryClient, QueryClientProvider } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;@tanstack/react-query&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; queryClient&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; QueryClient&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  defaultOptions: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    queries: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      staleTime: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 60&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 5&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      refetchOnWindowFocus: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; APIProvider&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;QueryClientProvider&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; client&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{queryClient}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {children}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;QueryClientProvider&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// useUser.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { useQuery } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;@tanstack/react-query&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetchUser&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response.ok) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;throw&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Network response was not ok&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useQuery&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    queryKey: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    queryFn: fetchUser,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// UserProfile.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UserProfile&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (isLoading) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Loading user info...&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (error) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Failed to load: {error.message}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;User Info&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Username: {user?.name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Email: {user?.email}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;13-swr&quot;&gt;1.3 SWR&lt;/h3&gt;
&lt;p&gt;Ultimate simplicity.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// useUser.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; useSWR &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;swr&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetcher&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(url);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;response.ok) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;throw&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Network response was not ok&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; response.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useSWR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, fetcher);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    user: data,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    isLoading,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    error&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// UserProfile.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UserProfile&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (isLoading) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Loading...&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (error) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Error: {error.message}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{user?.name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conclusion: To achieve the same functionality, &lt;strong&gt;SWR&lt;/strong&gt; offers the lowest cognitive load and minimal boilerplate. While &lt;strong&gt;React Query&lt;/strong&gt; is undeniably powerful, &lt;strong&gt;SWR&lt;/strong&gt; proves to be more than sufficient—and far more streamlined—for most everyday requirements.&lt;/p&gt;
&lt;h2 id=&quot;2-design-philosophy-reaching-the-essence&quot;&gt;2. Design Philosophy: Reaching the Essence&lt;/h2&gt;
&lt;h3 id=&quot;21-concise-and-elegant-taste&quot;&gt;2.1 Concise and Elegant Taste&lt;/h3&gt;
&lt;p&gt;SWR’s API design is spot on. This “less is more” aesthetic reflects the developers’ deep expertise and refined taste in API ergonomics.&lt;/p&gt;
&lt;h3 id=&quot;22-the-unique-url-as-key-design&quot;&gt;2.2 The Unique “URL as Key” Design&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unique Resource Identifier&lt;/strong&gt;: In Zustand, we manually name storage variables; in React Query, we maintain queryKey arrays. SWR, however, defaults to using the URL as the cache identifier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Back to Basics&lt;/strong&gt;: URL stands for Uniform Resource Locator. It is born to be the unique identifier for a resource, making it the perfect candidate for a cache key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3mutations-flexible-caching-strategies&quot;&gt;3.Mutations: Flexible Caching Strategies&lt;/h2&gt;
&lt;p&gt;Unlike typical request libraries, SWR enables caching strategies by default. Don’t worry—this isn’t a “cognitive load” but rather a way to achieve more flexible data handling with less code in common scenarios.&lt;/p&gt;
&lt;p&gt;In practice, &lt;strong&gt;useSWR&lt;/strong&gt; for data fetching is mostly “set it and forget it.” The real power lies in handling mutations with &lt;strong&gt;useSWRMutation&lt;/strong&gt;. First, let’s understand two key configurations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;populateCache&lt;/strong&gt;: Whether to update the local cache directly with the response from the mutation request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;revalidate&lt;/strong&gt;: Whether to trigger a fresh GET request to re-verify the data once the mutation is complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;31-revalidation-mode&quot;&gt;3.1 Revalidation Mode&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Configuration: revalidate: true | populateCache: false&lt;/li&gt;
&lt;li&gt;Workflow: PATCH Success -&gt; Trigger GET Request -&gt; Update UI&lt;/li&gt;
&lt;li&gt;Use Case: This mode introduces the most latency but is the most reliable. It is ideal for scenarios with complex backend logic (e.g., where a single update triggers side effects in other data fields).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isMutating&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useSWRMutation&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, updateUserFetcher, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  populateCache: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  revalidate: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;32-populate-cache-mode&quot;&gt;3.2 Populate Cache Mode&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Configuration: revalidate: false | populateCache: true&lt;/li&gt;
&lt;li&gt;Workflow: PATCH Success (returns new data) -&gt; Update UI using the PATCH response data&lt;/li&gt;
&lt;li&gt;Use Case: A balanced choice that requires only a single network round-trip. It offers a middle ground between speed and consistency, making it suitable for standard CRUD operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Profile&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useSWRMutation&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, updateUserFetcher, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    populateCache: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    revalidate: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ name: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;New Name&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; })}&gt;Quick Update&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;33-optimistic-updates&quot;&gt;3.3 Optimistic Updates&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Configuration: Uses optimisticData | populateCache: true | revalidate: false | rollbackOnError: true&lt;/li&gt;
&lt;li&gt;Workflow: Immediate UI update upon interaction -&gt; Initiate PATCH request -&gt; Finalize on success or rollback on failure.&lt;/li&gt;
&lt;li&gt;Use Case: Provides the fastest perceived response time. It is ideal for high-interaction scenarios like “Likes” or comments, where real-time feedback is critical.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isMutating&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useSWRMutation&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, updateUserFetcher, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  optimisticData&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;currentCache&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;currentCache,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    ...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;currentUser,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    status: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Saving...&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  populateCache: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  rollbackOnError: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  revalidate: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;34-optimistic-updates&quot;&gt;3.4 Optimistic Updates&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Configuration: revalidate: false | populateCache: false&lt;/li&gt;
&lt;li&gt;Workflow: Initiate DELETE or UPDATE request -&gt; UI remains unchanged with no cache updates or revalidation.&lt;/li&gt;
&lt;li&gt;Use Case: Ideal for background operations that don’t require immediate visual feedback on the current UI, such as deletions, telemetry, or event tracking.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isMutating&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useSWRMutation&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/user&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, deleteUserFetcher, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  populateCache: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  revalidate: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handleDelete&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (window.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;confirm&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Are you sure you want to delete your account?&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      alert&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Account deleted in the background (silent mode).&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (e) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      alert&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Deletion failed&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;4-conclusion&quot;&gt;4. Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;SWR&lt;/strong&gt; is a React data-fetching Hook that strikes a perfect balance between simplicity and power. It is not only lightweight and practical but also deepens one’s technical understanding of Uniform Resource Locators (URLs).&lt;/p&gt;
&lt;p&gt;When it comes to data fetching and caching, tools that require tedious Key configurations treat the request URL merely as a means to an end. In contrast, SWR treats the URL as the resource itself, reflecting a higher level of design maturity and taste. This is reminiscent of how Unix abstracts diverse hardware devices as “files,” or how Next.js ingeniously maps the file system to URLs. Truly great design often appears simple because it reaches the very essence of the problem.&lt;/p&gt;</content:encoded></item><item><title>Free AI IDE: VSCode + Roo + Mimo</title><link>https://daolanx.me/en/posts/post-10/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-10/</guid><pubDate>Fri, 19 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;i-why-stop-using-github-copilot&quot;&gt;I. Why Stop Using Github Copilot?&lt;/h3&gt;
&lt;p&gt;I’ve been using &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;Github Copilot&lt;/a&gt; for AI-assisted programming on VSCode for some time. As a built-in plugin, it integrates tightly with VSCode and offers friendly interaction, working well for lightweight local tasks. However, the free version has monthly token limits, and the paid version costs $10 per month, which is a bit expensive. Since my monthly tokens are used up, I decided to look for free alternatives to save costs.
&lt;img src=&quot;/images/posts/10/1-1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;My initial idea was to find a free model to connect with Github Copilot. However, after checking, I found that Github Copilot doesn’t support adding custom models freely - it only allows selection from limited model providers, and you need to upgrade to Pro to access more powerful models. This made me understand that the business model of such plugins is essentially acting as a terminal consumption scenario for models, somewhat like a utility provider.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/1-2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/1-3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Since commercial products offer good service but have limited customization, let’s see if there are open-source alternatives.&lt;/p&gt;
&lt;h3 id=&quot;ii-why-choose-roo-code-as-vscodes-ai-programming-plugin&quot;&gt;II. Why Choose Roo Code as VSCode’s AI Programming Plugin&lt;/h3&gt;
&lt;p&gt;Fortunately, VSCode’s plugin ecosystem supports open-source solutions.&lt;/p&gt;
&lt;p&gt;Currently, the most popular AI plugins for VSCode are Cline, Roo Code, and Kilo Code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/2-1.webp&quot; alt=&quot;&quot;&gt;
Community opinions generally agree that Cline has higher popularity, Kilocode is growing faster, and Roo Code is more flexible. Although all these tools claim to offer extensive configuration capabilities, from a personal selection perspective, this isn’t a technical research project that requires deep study - just choose what feels comfortable to use.&lt;/p&gt;
&lt;h4 id=&quot;1-cline&quot;&gt;1. Cline&lt;/h4&gt;
&lt;p&gt;The most popular Cline, looking at its Plan and Act modes in the interface, compared to the more scene-appropriate Github Copilot, I’m not quite used to it, so it’s on hold.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/2-2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;2-kilo-code&quot;&gt;2. Kilo Code&lt;/h4&gt;
&lt;p&gt;Kilo Code’s interaction is quite good - Code, Ask, Debug, and Agent modes are very suitable for usage scenarios. However, I encountered issues when connecting custom models, and autocomplete couldn’t run properly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/2-3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Kilo community &lt;a href=&quot;https://github.com/Kilo-Org/kilocode/issues/3458&quot;&gt;Issue#3548&lt;/a&gt; also reported this functionality problem. Since code completion is essential for productivity, I had to give it up.&lt;/p&gt;
&lt;h3 id=&quot;3-roo-code&quot;&gt;3. Roo Code&lt;/h3&gt;
&lt;p&gt;Finally, looking at Roo Code, the interface meets expectations, initialization isn’t as complex as the community said, and after customizing the model, code completion works normally, so I chose it!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/2-4.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I moved it to the right sidebar, then tried common file right-click operations, and everything worked normally.
&lt;img src=&quot;/images/posts/10/2-5.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/10/2-6.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/10/2-7.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;iii-subsequent-cleanup-hiding-github-copilot&quot;&gt;III. Subsequent Cleanup, Hiding Github Copilot&lt;/h3&gt;
&lt;p&gt;After installing Roo Code, you can hide Github Copilot to keep the interface clean.
&lt;img src=&quot;/images/posts/10/3-1.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/10/3-2.webp&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;/images/posts/10/3-3.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;To re-enable it later is also very simple - you can find the chat.disableAIFeatures option in VSCode settings and uncheck it.&lt;/p&gt;
&lt;h3 id=&quot;iv-why-use-mimo&quot;&gt;IV. Why Use Mimo&lt;/h3&gt;
&lt;p&gt;Actually, finding free models isn’t easy either. Most excellent models demonstrate their capabilities through Web products, but calling APIs costs money - the business model is truly like a utility provider.&lt;/p&gt;
&lt;p&gt;Recently, on December 17th, at the Xiaomi Human-Car-Home Conference, they released their large model base &lt;a href=&quot;https://mimo.xiaomi.com/blog/mimo-v2-flash&quot;&gt;MiMo-V2-Flash&lt;/a&gt;. Its highlights are that it has decent capabilities, fast speed, and extremely low prices ($0.1 per million input tokens and $0.3 per million output tokens). With competitive performance and reasonable pricing, this is very Xiaomi-like ^_^.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/4-1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/10/4-2.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Given similar capabilities, speed and price are what I value most. After trying the &lt;a href=&quot;https://aistudio.xiaomimimo.com/#/&quot;&gt;Web version&lt;/a&gt;, the initial speed and capabilities seem decent, and it’s currently free for over ten days. I’ll integrate and use it first to see the effects. If it becomes paid later, since the price is cheap, it can also serve as an affordable paid model option.&lt;/p&gt;
&lt;h3 id=&quot;v-configuring-roo-code-in-roo-code&quot;&gt;V. Configuring Roo Code in Roo Code&lt;/h3&gt;
&lt;p&gt;Integrating with Cline, Kilo Code, and Roo Code is also quite simple - just follow the &lt;a href=&quot;https://platform.xiaomimimo.com/#/docs/integration/cline-kilo-roo&quot;&gt;MiMo API Integration Documentation&lt;/a&gt;. It should be sufficient for lightweight programming assistance.
&lt;img src=&quot;/images/posts/10/5-1.webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>Next.js First Impressions: A Modern Web Framework for Frontend</title><link>https://daolanx.me/en/posts/post-9/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-9/</guid><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt; is currently a very popular Node.js Web framework on the market. After going through &lt;a href=&quot;https://youtu.be/k7o9R6eaSes?si=Uz-0fPLD01Q5VDkc&quot;&gt;Codevolution’s tutorial&lt;/a&gt;, my feeling is: its popularity is well-deserved. Next.js is a Web framework that is exceptionally well-suited for frontend developers. An excellent technical solution not only significantly boosts productivity—allowing you to do more with less—but its sound architecture and programming paradigm also help us establish a more accurate and concise cognitive understanding of relevant scenarios.&lt;/p&gt;
&lt;h3 id=&quot;i-starting-with-web-applications-and-urls&quot;&gt;I. Starting with Web Applications and URLs&lt;/h3&gt;
&lt;p&gt;Generally speaking, a Web Application refers to software that runs based on an internet browser. It consists of one or more pages, uses URLs to address and access page resources, and links pages together through hyperlinks to form a complete product logic.&lt;/p&gt;
&lt;p&gt;Therefore, implementing the definition and access of page URLs is the foundation of Web products. However, in reality, the URLs of many traditional Web servers are implemented based on code or configuration, sometimes even requiring multi-layer configuration due to layered architecture needs. In contrast, Next.js’s file-system-based routing design appears simple and intuitive when implementing the same functionality. This reminds me of the design of the Unix file system—good design is often simple, natural, and stands the test of time.&lt;/p&gt;
&lt;p&gt;Next is the parsing and consumption after resource addressing is completed. Resources are essentially information; information can be consumed from an API, or presented as a page with a specific template. Next.js is very clear in this aspect of design as well: it completes the parsing of resource types and the declaration of various states solely through folders and a limited set of filenames.&lt;/p&gt;
&lt;p&gt;In other words, Next.js can build a static site with complete URL definitions based solely on folders and filenames.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/9/1.webp&quot; alt=&quot;Next.js URL Design&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ii-common-problems-in-traditional-frontend-development&quot;&gt;II. Common Problems in Traditional Frontend Development&lt;/h3&gt;
&lt;p&gt;Next, let’s focus on page presentation and rendering. The evolution of frontend technology has followed a rather winding path; while continuously enhancing various capabilities, it has also constantly introduced new problems.&lt;/p&gt;
&lt;p&gt;Skipping the earlier eras of jQuery, CMD, and MVC, let’s start directly from the rise of React.&lt;/p&gt;
&lt;p&gt;As Web applications became increasingly rich in interaction, MVVM solutions like React began to gain popularity. This was not only because its declarative, rather than imperative, development paradigm was better suited for organizing and expressing rich interactions, but also because its component-based nature offered a more complete modularization strategy, which in turn provided the ability to decompose and reassemble complex scenarios.&lt;/p&gt;
&lt;p&gt;At the same time, however, React faced many integration issues. Most common Web frameworks on the market were primarily designed from a backend perspective and offered limited support for frontend view rendering; few frameworks could fully support React’s server-side rendering capabilities. This led to the widespread popularity of Single Page Application (SPA) architectures, where the page DOM serves as the entry point and JavaScript handles the entire rendering process. While these applications are well-equipped to handle rich interactions, they often face poor performance and SEO. To optimize performance, developers had to resort to local code-splitting and fine-tuning, or utilize micro-frontend solutions for decoupling. To improve SEO, some even adopted “spider-user separation” schemes to increase indexing. Meanwhile, the popular Webpack bundling solution of that era, while supporting various frontend build capabilities, also suffered from complex configurations and performance issues. These problems persisted for a long time, and it seemed that SPA performance and SEO had both hit a bottleneck.&lt;/p&gt;
&lt;p&gt;Furthermore, page rendering patterns existed in multiple modes, such as Server-Side Rendering (SSR), Static Site Generation (SSG), and Client-Side Rendering (CSR), making frontend development and technology selection highly fragmented. Once the general direction was set, it became extremely difficult to change or integrate different capabilities at a later stage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/9/2.webp&quot; alt=&quot;Common Problems in Traditional Frontend Development&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;iii-nextjs-innovations-in-programming-paradigm-and-architecture&quot;&gt;III. Next.js Innovations in Programming Paradigm and Architecture&lt;/h3&gt;
&lt;p&gt;Next.js’s innovation lies mainly in two points: the Isomorphic Programming Paradigm based on React, and a Unified Architecture that integrates rendering strategies.&lt;/p&gt;
&lt;p&gt;React’s design surpassed other libraries of its time because it did not rely on browser APIs, enabling support for multi-end rendering, including the server side. React’s popularity in the frontend has already proven its acceptance in the current trend of rich applications. The Next.js isomorphic programming paradigm, based on React, primarily minimizes the programming gap between frontend and backend, thereby reducing production costs.&lt;/p&gt;
&lt;p&gt;But actually, the benefit of the React isomorphic paradigm isn’t just about reducing frontend-backend differences and programming costs; its truly exciting power lies in the unified capabilities it brings.
&lt;img src=&quot;/images/posts/9/3.webp&quot; alt=&quot;Next.js Innovations in Programming Paradigm and Architecture&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;iv-how-nextjs-solves-traditional-frontend-problems&quot;&gt;IV. How Next.js Solves Traditional Frontend Problems&lt;/h3&gt;
&lt;p&gt;Looking back now, traditional frontend development suffered from numerous libraries, large gaps between frontend and backend, difficulties in selecting rendering strategies, and widespread performance and SEO issues in rich interactive applications. Next.js, based on innovations in programming paradigm and architecture, solves these problems from a native level, making everything simple again.&lt;/p&gt;
&lt;p&gt;First, the selection dilemma based on rendering strategies has disappeared. You can achieve excellent page-level Server-Side Rendering (SSR), Static Site Generation (SSG), and Client-Side Rendering (CSR) using only Next.js. This not only reduces technical differences caused by selection but also gives applications better capability for later changes and expansion.&lt;/p&gt;
&lt;p&gt;Second, SPA performance issues have also disappeared. Hybrid rendering based on RSC (React Server Components) combined with the server is outstanding. It can achieve rapid presentation of the overall page based on server-side rendering and progressively output local content via streaming. As far as I know, this effect is very good, offering better performance than similar micro-frontend schemes of the same period, while having almost none of the technical complexity of the latter. In other words, Next.js solves performance issues from a native angle, and the programming paradigm adds almost no burden.&lt;/p&gt;
&lt;p&gt;At this point, you will find that from a frontend perspective, Next.js has almost solved the problems that traditional frontend found hard to break through due to architectural limitations. It possesses the good paradigm of React for rich interactive applications, natively solves the negative issues of engineering and performance from an architectural standpoint, and possesses better extensibility and tension. It forms a very comprehensive capability, turning frontend technology and frontend-backend technology from a fragmented state into a simple, unified whole.&lt;/p&gt;
&lt;h3 id=&quot;v-new-capabilities-nextjs-brings-compared-to-traditional-solutions&quot;&gt;V. New Capabilities Next.js Brings Compared to Traditional Solutions&lt;/h3&gt;
&lt;h4 id=&quot;1-deep-integration-capabilities-with-backend-solutions&quot;&gt;1. Deep Integration Capabilities with Backend Solutions&lt;/h4&gt;
&lt;p&gt;Taking Clerk login as an example, by simply configuring the SDK, you can not only seamlessly integrate login and permission management functions for APIs and middleware but also integrate related buttons and forms at the page component level. This drastically reduces the cost of secondary development or self-research that was required in the past.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/9/4.webp&quot; alt=&quot;Deep Integration Capabilities with Backend Solutions&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;2-other-new-features&quot;&gt;2. Other New Features&lt;/h4&gt;
&lt;p&gt;Moreover, Next.js has some creative programming paradigms. For instance, frontend-backend data interaction implemented via useActionState allows the frontend and backend to no longer rely on tedious URL API implementation and parsing. Although it saves a lot of boilerplate code in small scenarios, and still awaits practical verification in finer-grained and larger-scale scenarios, it is indeed refreshing.&lt;/p&gt;
&lt;h3 id=&quot;vi-deployment-experience-engineering-assurance-from-the-vercel-ecosystem&quot;&gt;VI. Deployment Experience: Engineering Assurance from the Vercel Ecosystem&lt;/h3&gt;
&lt;p&gt;Next.js is developed by the Vercel team, and Vercel sponsors core open-source projects like React, ensuring close technical cooperation. The build process also adopts high-performance solutions, which work out of the box. Regarding deployment and hosting, using Vercel allows for one-click repository deployment and provides capabilities like runtime CDN acceleration, making the process from development to release seamless for simple products.&lt;/p&gt;
&lt;h3 id=&quot;summary-harvests-from-learning-nextjs&quot;&gt;Summary: Harvests from Learning Next.js&lt;/h3&gt;
&lt;p&gt;Therefore, Next.js is a Web framework that is particularly suitable for frontend learning. Learning it will bring many rewards:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cognitive Perspective&lt;/strong&gt;: Resource localization based on files, along with rendering capabilities based on paradigm and architectural innovations, brings a clearer and more concise understanding of scenario concepts, reducing development costs associated with traditional R&amp;#x26;D.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Technical Perspective&lt;/strong&gt;: Benefiting from innovations in its isomorphic programming paradigm and unified architecture, Next.js natively solves many problems of traditional frontend development and also optimizes deployment methods.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capability Perspective&lt;/strong&gt;: The capabilities of a single frontend role are often limited, but Next.js can provide the relatively complete capabilities needed to build Web products.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Market Perspective&lt;/strong&gt;: Next.js has high recognition in the market, which can lead to more job opportunities.
&lt;img src=&quot;/images/posts/9/5.webp&quot; alt=&quot;Harvests from Learning Next.js&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title>Blog Setup: Astro Framework + Vercel Deployment + Cloudflare DNS Proxy</title><link>https://daolanx.me/en/posts/post-8/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-8/</guid><pubDate>Sun, 30 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;i-why-choose-astro-framework&quot;&gt;I. Why Choose Astro Framework?&lt;/h3&gt;
&lt;p&gt;Initially, I planned to use Next.js to build the blog. Next.js is relatively suitable for front-end developers transitioning to full-stack, has a relatively smooth learning curve, and is widely used. Building a blog with Next.js would allow me to complete the project and deepen my understanding of the framework—a win-win situation.&lt;/p&gt;
&lt;p&gt;However, for content-driven websites like a blog, Astro is more suitable. First, its performance is excellent. Thanks to its primarily static page rendering and minimizing JavaScript overhead and dependencies, page performance is greatly enhanced. The official claim is that “it’s almost impossible to build a slow website with Astro.” Second, the development experience is friendly and the features are sufficient. The “Islands Architecture” allows for flexible integration of popular frameworks like React and Vue on top of static pages. It also supports multi-language and Markdown content management.&lt;/p&gt;
&lt;p&gt;Therefore, from the perspective of a better external product experience and simpler, more reliable internal development and maintenance, I chose Astro to build my personal blog.&lt;/p&gt;
&lt;h3 id=&quot;ii-why-choose-vercel-for-deployment&quot;&gt;II. Why Choose Vercel for Deployment?&lt;/h3&gt;
&lt;p&gt;I first encountered Vercel because the Next.js framework is developed by the Vercel team. Deploying a Next.js application on Vercel receives “native support,” making Vercel highly suitable as a deployment platform for initial full-stack learning with Next.js applications, which requires further exploration.&lt;/p&gt;
&lt;p&gt;The experience of deploying an Astro project on Vercel is also very smooth; it’s almost a one-click deployment from a GitHub repository. Similar platforms include Netlify and Cloudflare Pages. Netlify deployment is also smooth but offers no significant advantage over Vercel. Cloudflare Pages is also recommended by many, but it uses Bun for JavaScript builds, which can cause direct build errors for projects using scripts like pnpm, requiring extra configuration. In the absence of a clear advantage, I prefer simplicity to avoid additional configuration and steps. Simplicity means ease of mind and reliability.&lt;/p&gt;
&lt;p&gt;Furthermore, Vercel has good momentum, a strong team, supports the open-source ecosystem, adheres to the freemium model, and continues to improve its platform capabilities in the age of AI.&lt;/p&gt;
&lt;p&gt;Considering the free tier, excellent product experience, alignment with my learning path, and positive outlook, I chose Vercel as my deployment platform.&lt;/p&gt;
&lt;h3 id=&quot;iii-why-use-cloudflare-dns-proxy&quot;&gt;III. Why Use Cloudflare DNS Proxy?&lt;/h3&gt;
&lt;p&gt;Vercel provides capabilities such as CDN acceleration, HTTPS, rate limiting, firewall, and custom security headers for applications by default. However, actual testing shows that the access speed is still not ideal enough, this includes multiple reasons such as DNS pollution and node distribution.&lt;/p&gt;
&lt;p&gt;Cloudflare is one of the world’s largest CDN service providers. Using Cloudflare’s DNS proxy leverages its global CDN to significantly speed up website access. Although Vercel does not recommend third-party DNS (believing it increases link complexity), practical testing shows that the Cloudflare DNS proxy can indeed noticeably improve access speed within some places.&lt;/p&gt;
&lt;h3 id=&quot;iv-deployment-steps&quot;&gt;IV. Deployment Steps&lt;/h3&gt;
&lt;p&gt;Thanks to Vercel’s excellent developer deployment experience, the entire configuration process is quite simple.&lt;/p&gt;
&lt;h4 id=&quot;1-select-a-template-in-astro-and-generate-a-project-repository-on-github&quot;&gt;1. Select a Template in Astro and Generate a Project Repository on GitHub&lt;/h4&gt;
&lt;p&gt;Select a template from &lt;a href=&quot;https://astro.build/themes/&quot;&gt;Astro/theme&lt;/a&gt; and add it to your personal GitHub repository. I chose &lt;a href=&quot;https://github.com/sun0225SUN/astro-air&quot;&gt;astro-air&lt;/a&gt;. It has a minimalist style, supports Markdown content management based on MDX, and includes bilingual content switching.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/8/4-1.webp&quot; alt=&quot;Using Astro template&quot;&gt;
&lt;img src=&quot;/images/posts/8/4-2.webp&quot; alt=&quot;Using Astro template — astro-air&quot;&gt;&lt;/p&gt;
&lt;p&gt;Based on the template, generate your blog project repository on GitHub.
&lt;img src=&quot;/images/posts/8/4-3.webp&quot; alt=&quot;Using Astro template on Github&quot;&gt;
&lt;img src=&quot;/images/posts/8/4-4.webp&quot; alt=&quot;Creating a new Github repository&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;2-deploy-the-project-on-vercel&quot;&gt;2. Deploy the Project on Vercel&lt;/h4&gt;
&lt;p&gt;If Vercel is linked to GitHub, you can see the newly created project repository at &lt;a href=&quot;https://vercel.com/new&quot;&gt;vercel.com/new&lt;/a&gt;. Simply click Import and Deploy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/8/4-5.webp&quot; alt=&quot;Vercel project linked with Github repository&quot;&gt;
&lt;img src=&quot;/images/posts/8/4-6.webp&quot; alt=&quot;Vercel project deployment&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once deployed, the site can be accessed via the domain generated by Vercel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/8/4-8.webp&quot; alt=&quot;Vercel project dashboard&quot;&gt;
&lt;img src=&quot;/images/posts/8/4-9.webp&quot; alt=&quot;Accessing website using Vercel domain&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;3-set-the-domains-dns-provider-to-cloudflare&quot;&gt;3. Set the Domain’s DNS Provider to Cloudflare.&lt;/h4&gt;
&lt;p&gt;Assuming &lt;code&gt;xyz.com&lt;/code&gt; is the custom domain. Refer to a similar &lt;a href=&quot;https://www.nerdpress.net/how-to-transfer-your-dns-management-to-cloudflare/&quot;&gt;DNS migration document&lt;/a&gt; to set your domain’s DNS provider to Cloudflare.
&lt;img src=&quot;/images/posts/8/4-10-0.webp&quot; alt=&quot;Cloudflare DNS configuration 1&quot;&gt;
&lt;img src=&quot;/images/posts/8/4-10.webp&quot; alt=&quot;Cloudflare DNS configuration 2&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;4-link-the-custom-domain-on-vercel&quot;&gt;4. Link the Custom Domain on Vercel&lt;/h4&gt;
&lt;p&gt;In the Vercel project panel, add the custom domain to the deployed project.
&lt;img src=&quot;/images/posts/8/4-12.webp&quot; alt=&quot;Vercel project linked with custom domain&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add the corresponding &lt;code&gt;A record&lt;/code&gt; and &lt;code&gt;CNAME record &lt;/code&gt; at your domain’s DNS provider. You can click the “Automatic Configuration” button; if correctly identified, it will authorize a jump to the corresponding DNS service provider to complete the configuration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/8/4-13.webp&quot; alt=&quot;Configure DNS based on Vercel information 1&quot;&gt;
&lt;img src=&quot;/images/posts/8/4-14.webp&quot; alt=&quot;Configure DNS based on Vercel information 2&quot;&gt;&lt;/p&gt;
&lt;p&gt;At this point, once the DNS resolution takes effect, you can access the deployed website via your custom domain.&lt;/p&gt;
&lt;h3 id=&quot;5-use-cloudflare-dns-proxy&quot;&gt;5. Use Cloudflare DNS Proxy&lt;/h3&gt;
&lt;p&gt;In the &lt;code&gt;DNS/Records&lt;/code&gt; panel, toggle the Proxy status switch to accelerate web access speed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/8/4-15.webp&quot; alt=&quot;Enable proxy on Cloudflare&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, verify the acceleration effect in the &lt;code&gt;Speed/Observatory&lt;/code&gt; panel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/8/4-16.webp&quot; alt=&quot;View acceleration effect on Cloudflare&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>Why I&apos;m Sticking with Front-End Development</title><link>https://daolanx.me/en/posts/post-7/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-7/</guid><pubDate>Wed, 05 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;i-the-trigger-for-rethinking&quot;&gt;I. The Trigger for Rethinking&lt;/h2&gt;
&lt;p&gt;I worked as a Front-End Developer in a corporate setting for many years, but it wasn’t until I resigned that I truly found the opportunity to re-examine the question: “What is actually suitable for me going forward?” Many questions don’t have immediate answers; they require time and experience to settle.&lt;/p&gt;
&lt;p&gt;In the first few months after leaving, I felt a distinct discomfort from the sudden loss of life’s momentum. This was accompanied by anxiety—anxiety about the pause in income, and anxiety about having idle time. So, out of habit, I began polishing my resume, grinding algorithms, and memorizing standardized interview trivia.&lt;/p&gt;
&lt;p&gt;However, I ultimately chose not to continue down the path of job hunting.&lt;/p&gt;
&lt;p&gt;Part of this decision stemmed from the internal conflict I felt during the interview prep. While a job provides stable income, the process felt like selling practically all my time as a commodity, where my technical skills were merely quality-control checklists used to filter me. It felt heavily objectifying.&lt;/p&gt;
&lt;p&gt;But the primary reason was family. I need to take on the responsibility of childcare. In the internet industry, leaving early and returning late is the norm; balancing that schedule with taking care of a child is next to impossible.&lt;/p&gt;
&lt;h3 id=&quot;ii-why-stick-with-front-end&quot;&gt;II. Why Stick with Front-End?&lt;/h3&gt;
&lt;p&gt;Despite not seeking a corporate job, I decided to continue working in Front-End Development as a freelancer. This is based on two main reasons.&lt;/p&gt;
&lt;p&gt;The first is a direct, pragmatic reason: within the context of the social division of labor, it makes the most sense to do what I am experienced in. As a profession, Front-End provides value just like many other service roles. It is neither lowly nor something to show off about. If it provides value, it exists; if there is a market, there is an industry scale. Furthermore, every industry is stratified. You don’t have to be in the top 1% to survive; you can find a niche and a position that suits you.&lt;/p&gt;
&lt;p&gt;The second reason is a “cognitive sourcing” of what it means to be a programmer. I’ve been reading some books, and the one that impacted me most was “Hackers: Heroes of the Computer Revolution”.&lt;/p&gt;
&lt;p&gt;We are born into an environment where we are conditioned to absorb information from our surroundings. We have formed a consensus on the stereotypes of programmers: the rigid personality, the high-intensity overtime, and the idea that this is a “young person’s game” with an expiration date at age 35.&lt;/p&gt;
&lt;p&gt;But in reality, this is not the case. By using books to transcend time and look at the origins of programming—the development of the internet and computers, and the people behind these advancements—you see their cultural identity, their pursuits, their values, and their personalities. You discover that the reality differs vastly from the rigid stereotypes most people hold today.&lt;/p&gt;
&lt;p&gt;It reminds me of the movie “The Truman Show”. When Truman rows his boat and pierces the sky, he realizes it is actually the backdrop of a studio set. He has to tear open that curtain to find real information and seek the truth.&lt;/p&gt;
&lt;p&gt;You could say that most programmers, from their education to their employment, are molded as products of an industrial assembly line. But that is not the correct truth. The true history of computing is full of diverse individuals and heroism, encompassing their unique personalities and pursuits. Front-End is not just a solidified role in the division of labor; it is a technical product and a branch of technology. For personal development, becoming a Front-End developer to meet social needs is the starting point, not the end point. Only with a clearer cognition and a sense of identity can one walk steadier and further in this field.&lt;/p&gt;
&lt;h3 id=&quot;iii-the-challenge-of-starting-over&quot;&gt;III. The Challenge of Starting Over&lt;/h3&gt;
&lt;p&gt;The first challenge I faced was the technology itself. Although it is all “Front-End,” the tech stack in the open-source community is completely different from what is used inside companies. Leaving the corporate ecosystem meant almost relearning everything from scratch.&lt;/p&gt;
&lt;p&gt;However, after a few weeks of exploration, I found that community technology is actually more flexible and mature. The possibility of becoming a “Super Individual” is greater now. In the past, developing and deploying a simple app required coordinating various links and people; now, it can be done very quickly.&lt;/p&gt;
&lt;p&gt;The second challenge is the impact of AI. AI-generated content is everywhere now. My view is that, first and foremost, one must solidify the fundamentals. If you don’t understand the principles, you will be hollowed out and easily replaced. But if you understand the principles, AI becomes an efficiency booster—we cannot put the cart before the horse.&lt;/p&gt;
&lt;p&gt;Furthermore, rather than worrying, “if you can’t beat them, join them.” I should use AI to strengthen my own capabilities.&lt;/p&gt;
&lt;h3 id=&quot;iv-the-ultimate-question-of-continuous-reflection&quot;&gt;IV. The Ultimate Question of Continuous Reflection&lt;/h3&gt;
&lt;p&gt;Inscribed on the Temple of Apollo at Delphi is a maxim: “Know Thyself.”&lt;/p&gt;
&lt;p&gt;In this vast world, everyone’s life is vastly different. But as individuals, we face a choice: either drift with the current, or from the “boundless ocean, take only one ladle of water.” Making that choice—that trade-off—is what defines your own life.&lt;/p&gt;
&lt;p&gt;Life is short. How do we spend it so it doesn’t feel wasted? How do we find meaning?&lt;/p&gt;
&lt;p&gt;Perhaps continuously doing things that you find valuable and interesting is enough.&lt;/p&gt;</content:encoded></item><item><title>The Significance of a GAP Year</title><link>https://daolanx.me/en/posts/post-6/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-6/</guid><pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Having been away from the workplace for more than a year, I’ve realized that making real changes truly requires an extended break, and this period has its own profound significance.&lt;/p&gt;
&lt;h3 id=&quot;1-relaxing-the-body-and-mind&quot;&gt;1. Relaxing the Body and Mind&lt;/h3&gt;
&lt;p&gt;Putting down everything, important or otherwise, slowing down, and returning to a state of calm are essential for an objective understanding of life.&lt;/p&gt;
&lt;h3 id=&quot;2-forgetting-the-past&quot;&gt;2. Forgetting the Past&lt;/h3&gt;
&lt;p&gt;With the extremely slow passage of time, one should forget the behaviors and thoughts from past work and life. Such changes can only occur within a deeply purified mind. Just as in Jin Yong’s novels, to learn Tai Chi, one must first forget; only by forgetting can one truly learn, and only an empty cup can hold water.&lt;/p&gt;
&lt;p&gt;Forget your previous routine, forget emotions, forget labels, forget abilities, forget everything.&lt;/p&gt;
&lt;p&gt;However, forgetting is not losing. Those experiences and abilities will drift away because they are freed from their previous pull, and then, through new understanding, they will reaggregate in a new form.&lt;/p&gt;
&lt;h3 id=&quot;3-cultivating-metacognition&quot;&gt;3. Cultivating Metacognition&lt;/h3&gt;
&lt;p&gt;To set everything aside and start anew, the first step is to correct the fundamental concepts and understanding of things.&lt;/p&gt;
&lt;p&gt;By using good books as a reference for thinking, you should question the cognitions you have established about work and life, and identify those incorrect ones. For example, the notions that work can make you money, that diligence is necessary for doing things, or that learning simply means memorizing answers for exams, are all wildly wrong. In reality, work often can’t make you truly rich; instead, it offers a relatively low leverage ratio. True financial freedom is about freedom of time and choice. More importantly, doing things is about doing the right things, not just being diligent. Learning primarily aims to cultivate thinking and analysis skills, not to remember standard answers.&lt;/p&gt;
&lt;p&gt;Therefore, the common sense that we follow without question might actually be flawed. The Truman Show is a fictional movie, yet it truly exists in our daily lives. The more important question to explore is how to build your own trustworthy dependencies in a world that is inherently untrustworthy.&lt;/p&gt;
&lt;p&gt;Only by continuously improving your metacognitive ability can you find a relatively reliable anchor point, upon which you can build a more reliable and broader set of capabilities.&lt;/p&gt;
&lt;p&gt;For example, I’ve always felt a sense of rejection and tediousness toward technical professional education and work. Now, with more insight, I can explain it: it’s because what they teach is incomplete. They only teach professional knowledge, but they fail to teach the technical culture, value metrics, and, more importantly, the ability to learn and analyze.&lt;/p&gt;
&lt;p&gt;Why do they design education and work this way? A possible reason is simply limited teaching quality, but the greater probability is that the curriculum has been compromised by the need for selection, industrial demands, obedience, and various other designs, making it impure. Simply put, it’s not designed for your personal development; it’s subservient to some larger agenda—they need to mass-produce industrial workers.&lt;/p&gt;
&lt;p&gt;Hackers: Heroes of the Computer Revolution was deeply insightful. It not only chronicles the history of computers from research to home use but also vividly documents the people behind the computer’s development, how their abilities were formed, their values, their subculture, and its dilution and return over time. I also read Linus Torvalds’ autobiography, and you realize that technology is not about step-by-step instruction, standard-answer exams, cold documentation, or all-night overtime. Technology has heart and soul; it’s driven by people who are full of curiosity and courage for the world. They love life, love technology, focus on what is interesting, useful, and tasteful. A technology project is not just a pile of code and execution; behind both software and hardware are abundant wisdom, passion, and personality. You can also trace the origins of many current technical forms and cultures through their experiences. For instance, the now-popular GitHub can likely trace its roots to the culture and spirit of the MIT hackers—those freely shared rolls of paper tape. Similarly, today’s forums and online shopping may originate from Lee Felsenstein’s “Community Memory” decades ago. The times are advancing, and technology is evolving, but everything is not just code; it also contains the polish of time and the warmth of people. By following them through books and images, understanding the culture and origins behind technology, you can truly connect the past and the future to genuinely understand and engage with technology itself.&lt;/p&gt;
&lt;p&gt;Therefore, personal development should involve clearly seeing the issues within these frameworks, discovering the bugs in the manipulated world, and hacking everything.&lt;/p&gt;
&lt;h3 id=&quot;4-rebuilding-the-personal-operating-system&quot;&gt;4. Rebuilding the Personal Operating System&lt;/h3&gt;
&lt;p&gt;Based on more accurate metacognition and associations of things, I am rebuilding my personal understanding, moral compass, values, and capabilities.&lt;/p&gt;
&lt;p&gt;Then, based on these new personal capabilities, I will rebuild my social life, personal life, technical skills, and investment strategies.&lt;/p&gt;
&lt;p&gt;Only by undergoing a complete transformation can one achieve true elevation.&lt;/p&gt;
&lt;p&gt;I’ve observed a common trait among successful people in both technology and investment: they all possess strong metacognitive abilities. They are certainly not focused on diligence or hard work, nor are they merely greedy for wealth. They are more concerned with understanding and hacking the entire system, hoping to gain a certain return and achievement through their understanding. That is, they use their metacognitive ability to deeply understand the system, then continuously iterate and verify their understanding, thus forming a positive feedback loop and a deeper comprehension of the world.&lt;/p&gt;
&lt;h3 id=&quot;5-doing-the-right-things&quot;&gt;5. Doing The Right Things&lt;/h3&gt;
&lt;p&gt;Doing the right things is significantly more important than merely doing things correctly. I’ve seen this concept in the books of Li Xiaolai and ‘Left-Ear Rabbit’ (Zou Xin), as well as in the actions of people like Steve Jobs and Steve Wozniak. This applies well to both investing and technology.&lt;/p&gt;
&lt;p&gt;Doing the right things is paramount, even if it means moving a little slower. What currently might not look like the right path, might, in fact, be the only true path.&lt;/p&gt;
&lt;h3 id=&quot;6-recommended-reading&quot;&gt;6. Recommended Reading&lt;/h3&gt;
&lt;h4 id=&quot;cognitive-awakening&quot;&gt;Cognitive Awakening&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The Road to Wealth Freedom&lt;/em&gt; by Xiaolai Li&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;investment-cognition&quot;&gt;Investment Cognition&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Rich Dad Poor Dad&lt;/em&gt; by Robert Toru Kiyosaki&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;technology-cognition&quot;&gt;Technology Cognition&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Hackers: Heroes of the Computer Revolution&lt;/em&gt; by Steven Levy&lt;/li&gt;
&lt;li&gt;&lt;em&gt;iWoz: Computer Geek to Cult Icon&lt;/em&gt; by Steve Wozniak&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Just for Fun: The Story of an Accidental Revolutionary&lt;/em&gt; by Linus Torvalds&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Left Ear Listens to the Wind&lt;/em&gt; by Hao Chen&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>What Parenting Taught Me</title><link>https://daolanx.me/en/posts/post-5/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-5/</guid><pubDate>Mon, 18 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Another half a year has passed, and after a few family twists and turns, I am now a full-fledged stay-at-home dad, in charge of meals, daily routines, and figuring out how to make money. The most important thing on my mind right now is waiting for my child to start kindergarten in a month.&lt;/p&gt;
&lt;p&gt;I used to think that parenting was a hard and tedious job, but it turns out I’ve learned so much.&lt;/p&gt;
&lt;p&gt;Maybe an outsider sees more clearly. Before, almost everything I did revolved around work, and I subconsciously viewed everything else as a distraction or an afterthought. Then I read some books that had a specific, biting critique for this: they call it being a workaholic, sacrificing your time and life to become a machine for work. What’s more ironic is that this state isn’t born from a passion for work, but from a place of escape and fear.&lt;/p&gt;
&lt;p&gt;Stepping away from work these past six months has allowed me to think more deeply. This article has been written on and off for a long time. My thoughts are a jumble, and my writing skills are limited, but I still want to put it down as a record of what I’ve gained during this time.&lt;/p&gt;
&lt;h3 id=&quot;1-the-most-fundamental-thing-rebuilding-familys-emotional-foundation-home-is-where-the-heart-is&quot;&gt;1. The Most Fundamental Thing: Rebuilding Family’s Emotional Foundation. Home is Where the Heart Is&lt;/h3&gt;
&lt;p&gt;The foundation of a home is emotional and spiritual connection. To form a family, the most important thing is the spiritual bond—at a minimum, mutual trust and support. Even better, mutual appreciation and helping each other grow. These spiritual elements give you a sense of purpose and belonging when you’re tired and facing difficulties. A basic sign of a home is a place that feels safe and secure, and people who are warm and kind.&lt;/p&gt;
&lt;p&gt;Conversely, if family members constantly fight, attack, and tear each other down, there is no spiritual foundation for a home. Even with great material support, it’s just an empty shell that can easily fall apart. It’s a shame, but principles are just principles; you only truly understand them through experience.&lt;/p&gt;
&lt;p&gt;Parenting has also shown me the pure, unconditional trust and dependence a child has on their parents. They have no suspicion or reservation; every emotion and action is fed back to you without holding anything back. A while ago, I was driving with my child to get away from a local water contamination incident. In that moment, I suddenly felt that this—a car, a child, diapers, and a bottle—was home. It was enough for me to feel safe wandering anywhere. Nothing else mattered.&lt;/p&gt;
&lt;h3 id=&quot;2-solving-survival-issues-rebuilding-the-familys-financial-foundation-and-using-capital-leverage&quot;&gt;2. Solving Survival Issues: Rebuilding the Family’s Financial Foundation and Using Capital Leverage&lt;/h3&gt;
&lt;p&gt;First, there was the problem of making money. How do you eat and live without a salary?&lt;/p&gt;
&lt;p&gt;The first few months, I was truly panicked. I felt that if I didn’t have a job, I had no income, so I started looking for work. I went through a lot. Some jobs had too much intensity; I considered them for the money, but my heart was against it, and thankfully, they didn’t work out. Some jobs seemed easy to get but might not be stable. And some looked high-paying, but the industry might be a gray area. After a few months, I felt that many jobs were like a fortress; most people break their heads to get in, only to continue the pattern of sacrificing personal space and time for a paycheck.&lt;/p&gt;
&lt;p&gt;Since I had to be a full-time caregiver due to a family change, I simply didn’t have the regular hours for a typical job. So I decided to just “lie flat” for a while and wait until my child started kindergarten. I spent my free time reading, and that series, Rich Dad Poor Dad, opened up my financial intelligence.&lt;/p&gt;
&lt;p&gt;As I studied the ESBI quadrant and leverage, I understood the limitations of the employee model. Although it’s a habitual way of thinking for many, it has low leverage, similar to low-risk investments. Behind the stability, you’re sacrificing profit and taking on the risk of limited personal growth. In reality, it’s not even that stable—if you stop working, your income disappears. There’s also a contradiction in career design: it assigns you a defined value and a limited scope, yet encourages you to think and act holistically, leading to an awkward situation.&lt;/p&gt;
&lt;p&gt;Later, I started learning about investing and got into the stock market. I was lucky enough to open a bank account in Hong Kong and a brokerage account, beginning my investment journey. It was at the end of last year’s bull market, and the stocks I bought went down. I lacked risk awareness and dabbled in margin trading, options, and shorting, which resulted in a loss of tens of thousands of dollars. Thinking about how this was all my hard-earned savings, which I normally wouldn’t even spend, and how it was now gone, I couldn’t sleep for a long time. It was my darkest moment. In the end, I came to terms with it. There was no point in being sad. I had to look forward and find a solution. After getting back on the investment track, I intensified my learning. The process has been full of twists and turns, but now I have some income and am temporarily financially secure.&lt;/p&gt;
&lt;p&gt;With the practical knowledge of finance and investing, I feel like my wealth is accumulating and my spending is decreasing. Through decluttering, high-quality consumption, and asset management, everything is slowly getting on the right track.&lt;/p&gt;
&lt;h3 id=&quot;3improving-quality-of-life-reshaping-the-foundation-of-family-life&quot;&gt;3.Improving Quality of Life: Reshaping the Foundation of Family Life&lt;/h3&gt;
&lt;p&gt;First, what to eat? The old answer was simple: the cafeteria or takeout. Now, I’ve started making some home-cooked meals, for a simple reason: to save money and to have more freedom and control over my health.&lt;/p&gt;
&lt;p&gt;Where to go? I had no choice before; I’d pay high prices on weekends, get stuck in traffic, and be surrounded by crowds. Now I’m free to choose, and I’ve found that on weekdays, places are less crowded, the scenery is beautiful, and tickets are cheaper. Many places are even free and provide a better experience; the best places aren’t necessarily the most expensive or the most crowded.&lt;/p&gt;
&lt;p&gt;What to use? I’ve started paying attention to that, too. First, I did a huge declutter and got rid of a lot of things. My life became more minimalist.&lt;/p&gt;
&lt;p&gt;If something is used very little, even if I like it, I sell it if it doesn’t add much meaning to my life.&lt;/p&gt;
&lt;p&gt;If something is expensive but the quality is just so-so, I replace it with a moderately priced but better-quality item for the same purpose. I focus on high quality, not just high prices.&lt;/p&gt;
&lt;p&gt;Redundant and duplicate items that are left unused in a corner are a waste of space. My place isn’t big, so I resolutely throw them away to reclaim the space.&lt;/p&gt;
&lt;p&gt;Then, I added more color and vibrancy to the previously “Muji” style of minimalist decor. For example, a forest painting in the living room, a mouse tissue box, a monster tablecloth on the dining table, a dinosaur toothbrush, a dog soap dish, a drawing board wall, and a photo wall. I hope these things will subconsciously add more color and warmth to my child’s life.&lt;/p&gt;
&lt;h3 id=&quot;4-going-beyond-physical-space-developing-social-connections&quot;&gt;4. Going Beyond Physical Space: Developing Social Connections&lt;/h3&gt;
&lt;p&gt;The social relationships we typically have are often passive: family, classmates, or even workplace colleagues. The problem with them is that you have little to no choice in the matter.&lt;/p&gt;
&lt;p&gt;Fortunately, with the development of the internet, we can use the information age to form relationships that go beyond physical space. I envision developing three types of social connections: functional, appreciation-based, and interest-based. Functional ones will be the majority, providing information, services, or social connections. The appreciation-based ones may be few, but they are crucial—connecting with people you admire and find interesting. Interest-based ones, like those formed through games, are important because people aren’t machines; we can be more human with hobbies and character.&lt;/p&gt;
&lt;p&gt;However, developing social connections is just a method. The core is to constantly enrich yourself, continuously cultivate your personal abilities, and enhance your self-worth. This allows you to build a personal brand and social circle from the inside out. I don’t mean just making yourself a tool; you should be a flesh-and-blood person who shares work, hobbies, and life. This is the prerequisite for building diverse social connections.&lt;/p&gt;
&lt;p&gt;What’s been interesting lately is becoming a stock shareholder in a popular forum after just half a year of investing. It’s brought some unexpected surprises and a boost in confidence. Investing was a completely new area for me, but because I wanted to make money in a more stable and effective way, I put my learning skills to work. I started actively reading books and forum posts, and diligently documented my trades and insights on the forum, primarily for my own review.&lt;/p&gt;
&lt;p&gt;I never expected my following to grow so much over the past six months. Recently, the forum administrators contacted me to become a shareholder. They said they had been observing me for a long time and believed I had a relatively mature trading system, a stable core, and that I was consistent in my words and actions. They noted my large following and, despite my limited trading experience, saw potential for growth.&lt;/p&gt;
&lt;p&gt;This is probably the first time I’ve been recognized in an unfamiliar field and outside of my usual social circle. I’ll admit I’ve been experimenting with some personal branding techniques on the investment forum, like writing high-quality posts and sharing my experiences, and commenting on big-name investors’ articles to get more visibility. I even got some unexpected recommendations from well-known investors, which led to a surge in followers. However, from a deeper perspective, I wasn’t doing it just to become a big shot; it was more that I genuinely wanted to do well, and I simply went with the flow and paid attention to some personal branding methods.&lt;/p&gt;
&lt;p&gt;I also understand that to be a good shareholder or moderator, it’s not about being an isolated expert who lectures others while they just listen. It’s about bringing people together to better understand and learn about stocks and investing. Fostering a space for discussion and learning is more important than simply making statements myself.&lt;/p&gt;
&lt;p&gt;On a deeper level, in terms of meaning, while this is just a reputation, it’s a reminder that life is a process. Where you invest your attention, that’s where your life is. That’s why I don’t want to drift aimlessly or waste my time. If I’m going to do something, I want to do it as well as I can.&lt;/p&gt;
&lt;h3 id=&quot;5-three-abilities-and-the-ultimate-question&quot;&gt;5. Three Abilities and the Ultimate Question&lt;/h3&gt;
&lt;p&gt;After everything I’ve experienced in the past six months, I feel like my life hasn’t fallen apart; in fact, it seems to be moving in a better direction. It’s not just luck. I’ve realized that three abilities are at work, and they remain even when work is taken away. They are: the courage to change, the ability to learn, and a transcendent mindset.&lt;/p&gt;
&lt;p&gt;The Courage to Change: Don’t live a passive or aimless life. Always have the courage to change things, to make your life, yourself, and the people and things around you better.&lt;/p&gt;
&lt;p&gt;The Ability to Learn: Whether it’s investing or cooking, you can learn and put it into practice, instead of just listening to what others say. A person’s individual time and experience are limited, but you can use books and media to transcend physical space and time to gain a variety of rich experiences and knowledge.&lt;/p&gt;
&lt;p&gt;A Transcendent Mindset: I often step out of my own life to look at and summarize it from a “third-person” perspective. This makes me think of certain philosophical concepts and sigh at how quickly time passes, which in turn allows me to see my own life more clearly.&lt;/p&gt;
&lt;p&gt;I recently finished reading The Three-Body Problem. The line, “Give civilization to the years, not years to civilization,” deeply moved me. Behind that shock is the sadness of “lamenting the brevity of my life while admiring the endlessness of the great river.” Within the grand framework of the universe as paper and time as a pen, even a civilization is insignificant, let alone a single human life. No matter how many twists and turns, it is just a speck of dust, and everything seems meaningless.&lt;/p&gt;
&lt;p&gt;However, the author also wrote in the novel, “We had a happy life.” Those few words, though small, are full of meaning and brilliant. The universe, time, years, civilization—these are all grand narratives, the vocabulary of the Creator. But accepting the brevity of life with dignity and grace is the human answer. Socrates’s phrase, “Know thyself,” still resonates powerfully today.&lt;/p&gt;
&lt;p&gt;Returning to real life, I believe I need to adjust my attitude toward life, work, and investing. First, I should abandon the false ideas and distractions of professional life—the pursuit of profit, anxiety, and the culture of “involution.” I must remove the labels and the “tool-like” nature of work. I should first return to my own humanity, and become a real person with flesh and blood, with strengths, weaknesses, hobbies, and tastes. I need to have a complete and healthy personality. On that basis, I can then build my life and professional skills, develop hobbies and talents, provide services to society, exchange value, and get the compensation I deserve—no more, no less.&lt;/p&gt;</content:encoded></item><item><title>2024 H2: Job Hunt, Indie Dev, Invest, And Babysitting</title><link>https://daolanx.me/en/posts/post-4/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-4/</guid><pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Written on New Year’s Eve, this is a rough summary of the events and reflections from the past half of 2024.&lt;/p&gt;
&lt;h3 id=&quot;job-hunting-experience&quot;&gt;Job Hunting Experience&lt;/h3&gt;
&lt;p&gt;After leaving my position, I spent the first few months in the job market as a matter of habit. I brushed up on my knowledge through problem-solving and review of basic concepts, and attended interviews at ByteDance, foreign companies, and small startups. I went through interviews with three different teams at ByteDance, generally making it to the middle or later stages. Overall, about half of the interviewers seemed to recognize my potential while the other half did not. This made me realize that the capabilities within ByteDance’s teams are also uneven. I also interviewed for remote positions at two foreign companies. One of them didn’t go well in the final round, and I felt the interview process was quite unprofessional, probably due to their being a startup with an inconsistent team. The other one didn’t make it past the second round. Their technical skills were impressive, and I was convinced that I was outmatched. Additionally, I interviewed at a small startup. They seemed to have a system in place, but I soon discovered that it was more about deception and manipulation. They had some absurd ideas like not providing social insurance and housing fund, asking candidates to take risks and even invest in the company. I communicated with their technical team under the pretext of having a chat, and found that they were actually quite pragmatic but rather naive, seemingly falling for such tactics. They said they would contact me later but ended up blocking me, which gave me a glimpse into the complexity of the recruitment environment. I also reached out to a few cross-industry internet positions, but there was no follow-up. Eventually, I stopped looking at job postings.&lt;/p&gt;
&lt;p&gt;To be honest, the interview process was emotionally complex. On one hand, there was the anxiety of the interviews themselves. On the other hand, I was dissatisfied with the 996 work culture and didn’t want to join another company with such a working pattern. It felt like being in a dilemma. At that time, I was only confused by my emotions and didn’t understand the reasons behind them.&lt;/p&gt;
&lt;h3 id=&quot;new-understanding-of-work&quot;&gt;New Understanding of Work&lt;/h3&gt;
&lt;p&gt;After several months of learning, I came to understand the reasons behind my internal conflicts and complexities.&lt;/p&gt;
&lt;p&gt;Firstly, work itself has its limitations. Since occupations are based on division of labor, the value one can produce is confined to that specific job role. Being restricted by job design and lacking complete information on the business model, the output value is inherently limited. Some organizations encourage individuals to go beyond their positions to demonstrate their value. However, this is destined to have a low success rate and can easily lead to distorted actions without even realizing it.&lt;/p&gt;
&lt;p&gt;Secondly, there is a lack of leverage in work. Work is essentially about trading time for money, with no leverage involved. Many products can make a profit by selling multiple copies due to the decreasing marginal cost. But as an employee, you only receive a single salary.&lt;/p&gt;
&lt;p&gt;Moreover, contemporary work lacks long-term security. It is said that work as we know it was born in the industrial age. Before industrialization, people lived in a self-sufficient agrarian economy with little factory work. After industrialization, large-scale production led to the emergence of a working class and job specialization. At that time, there were indeed more comprehensive company benefits. However, in today’s internet age, although the working model has been continued, company benefits have become less stable. In other words, work, which seems to be the most common way of earning a stable income, actually has many flaws that are worth paying attention to.&lt;/p&gt;
&lt;p&gt;I believe that the ideal job to join is one with a positive-sum and ecological model. Positive-sum means that everyone works together to get things done, rather than being trapped in a zero-sum game of performance and competition. Ecological refers to everyone performing their own roles and being able to form a business closed loop. I have experienced some teams in the past, and now I can finally describe them as positive-sum and ecological teams. In such an environment, although work appears to be just a salary without leverage, the process of mutual cooperation and positive-sum efforts can bring satisfaction in terms of products, capabilities, and emotions. There is also the opportunity to grow the pie in a positive-sum manner. If anyone is fortunate enough to join this trend and team, please cherish it and give full play to your potential. Even if you end up with nothing in the end, life is a process, and having this experience is invaluable.&lt;/p&gt;
&lt;h3 id=&quot;new-understanding-of-technology&quot;&gt;New Understanding of Technology&lt;/h3&gt;
&lt;p&gt;As a technical professional, one’s capabilities and career trajectory can often be misdirected under a specialized job role, making it challenging to achieve sustained and substantial development. After I left my previous position and surveyed numerous similar roles in the market, I realized that the technologies we utilized in-house were not widely adopted in the broader market. Conversely, the technologies that were popular in the market were not implemented within our company. Ironically, we spent considerable time on phased optimizations and repeatedly patching certain technical issues, when in fact, these issues had already been effectively resolved by the popular market technologies, which had even integrated the solutions into their frameworks. It’s both ridiculous and lamentable that many people in the company used these subpar, market-lagging technologies to report to their bosses, thereby securing profits and promotions. Instead of being utilized to create better products and pursue more advanced productivity, technology had degenerated into a tool for show and appeasement.&lt;/p&gt;
&lt;p&gt;After the division of labor, there is also a certain degree of spiritual misleading for technical practitioners. Boring university education, stereotyped job interviews and screwing jobs in factories make people feel that technology is very monotonous, boring, and related to internal competition and overtime. But in fact, the true technical connotation has nothing to do with these negative factors.&lt;/p&gt;
&lt;p&gt;So what is real technology, and the ability, spirit, and even culture of technical people? I watched a lot of videos and interviews of technical experts, and also read some hacker and open source history. The ones that had a deep impact on me were Apple’s Wozniak and Linux’s Levis. Their understanding of technology was very simple, for fun or value. I think simplicity is the best way, and I can use this to wash away my previous misunderstandings about technology, and as a standard for learning technology for a long time.&lt;/p&gt;
&lt;h3 id=&quot;one-person-company&quot;&gt;One-person Company&lt;/h3&gt;
&lt;p&gt;Since it is difficult to find a suitable job and jobs themselves have limitations, is there a better way to participate in society and earn income?&lt;/p&gt;
&lt;p&gt;Through some channels, I learned that a one-person company is indeed good from a logical point of view. It runs a closed business loop by itself, fully realizes its value, and has free time. The greater endorsement comes from “Rich Dad Poor Dad”, who mentioned the earning dimensions of several quadrants of ESBI. In fact, a one-person company is the B-type earning method he recommends, with better space and leverage.&lt;/p&gt;
&lt;p&gt;But considering my own abilities, I feel that the threshold for implementation is actually quite high, because not only does it require excellent technical capabilities to improve productivity and reduce costs, it also requires complete product design, marketing promotion and operational capabilities.&lt;/p&gt;
&lt;h3 id=&quot;independent-development&quot;&gt;Independent Development&lt;/h3&gt;
&lt;p&gt;Is there a compromise? Perhaps independent development is more suitable for stage transformation.&lt;/p&gt;
&lt;p&gt;The book “Rich Dad” says that independent developers make money in the S quadrant, which means that although it is better than the E-type employee model and can get a relatively complete individual value, it is also a life of hard work, because the individual’s time and energy are limited after all. But in fact, I read books such as “Going it Alone”, “Freelancer Survival Guide”, “Personal Brand Building: From 0 to 1 Low-Cost Entrepreneurship”, and “One-Person Company”, and found that individual workers are not that stupid. Of course, they don’t want to be hard-working. In fact, they will transform in the later stage. Some make products, and some start small companies. In fact, all roads lead to Rome.&lt;/p&gt;
&lt;p&gt;The biggest difference between independent developers and employees is that they can face market demand directly, actively improve production technology, and obtain compound interest by accumulating customers and solutions; while in the employee model, the partners and production processes are often determined, and individuals have no freedom of choice and cannot accumulate compound interest.&lt;/p&gt;
&lt;p&gt;Just do it. Since the domestic market is too competitive, let’s look at the international market. I plan to make money with upwork. There are two main things to learn first. One is to be familiar with popular technologies and some cases on the market, and the other is to get used to learning and communicating in English. It seems harder than finding a job, but in fact, whether it is technical cases or English, they can accumulate compound interest. It’s hard at first and easy later. This is why I changed to a bilingual blog.&lt;/p&gt;
&lt;p&gt;I also took a rough look. In fact, the competition on upwork is quite fierce. But my goal is not to be the best group of people. I think it’s enough to outperform the average group of people. Rowling, who wrote Harry Potter, also said that if you experience failure because of timidity, your life is a failure by default. Summarize experience for success and lessons for failure. Perseverance should be rewarding.&lt;/p&gt;
&lt;h3 id=&quot;the-road-of-investment&quot;&gt;The Road Of Investment&lt;/h3&gt;
&lt;p&gt;This was something I thought of before the New Year. Since the timeline for personal companies and independent development is relatively long, is there any way to earn some income? I thought of investing. Because in terms of revenue, you need to give full play to your abilities and resources, and after you come out, you can get some funds. The “Rich Dad Poor Dad” series also gave me some inspiration.&lt;/p&gt;
&lt;p&gt;So I went to Hong Kong to apply for a card and officially started investing to make some money, hoping to give myself more room financially. Although it has been a few months, I have learned a lot. At first, I invested in stocks recommended by several friends in the US stock market. I made money that month and felt that making money was quite easy. But the good times didn’t last long. After a month, it started to fall and lose money. I started to continue learning and found that this type of stock is actually leveraged, that is, the bull market is naturally good, but the bear market is very difficult to make up. In terms of operation, I also spent five figures to buy a lesson. Because I learned about the method of making money without money in the book “Rich Dad Poor Dad Stock Investment from Beginner to Mastery”, but I didn’t understand it at the time. Suddenly one day, there was a very negative news, and I suddenly understood that it was called shorting. I thought the news was very negative and I should take advantage of it, so I raised all the funds to short. It was very coincidental that the Chinese and American heads of state had a phone call at that time, which brought greater benefits, so the stocks I shorted and sold rose straight after the opening. From panic to closing positions in batches, I ended up losing 5 figures, which taught me a solid lesson. I didn’t make money in the first place, and I ended up losing more.&lt;/p&gt;
&lt;p&gt;If you don’t understand, continue to learn. After reading books like “Investing Like Buffett”, plus the lessons learned last time, I learned that first of all, you still have to have a correct mentality in operation, don’t be emotional, think rationally, and be self-disciplined and disciplined. Secondly, when selecting stocks, you still have to pay attention to the basics, choose good companies, make money you understand, and try not to short, leverage, fluctuate, or speculate. Because good companies will naturally bring many good opportunities, and grow with good companies. Short-term and volatile, the news brought by leverage is indeed enviable and attractive, but it is best not to touch money you don’t understand. Maybe I will grasp some opportunities or make some speculations within the rational range when I understand more in the future, but don’t rush at this stage, take your time, it will be faster.&lt;/p&gt;
&lt;p&gt;After another month, I have almost recovered my previous losses due to value investment. I hope this is a good start.&lt;/p&gt;
&lt;h3 id=&quot;full-time-babysitting&quot;&gt;Full-time Babysitting&lt;/h3&gt;
&lt;p&gt;Plans can’t keep up with changes. Due to some family reasons and twists and turns, I need to take care of my baby full-time for a period of time. I have to eat, drink, defecate, play, and basically can’t rest for 12 hours. Time has become very fragmented, so fragmented that I can’t support independent development. Even writing this blog was written intermittently during the noise of the baby.&lt;/p&gt;
&lt;p&gt;In the scattered time, you can read books on your mobile phone. During this period, there are two books that impressed me the most. One is “Navarre’s Treasure Book”. He mentioned that we should accept the status quo and not fight. We should accept the status quo first and face the problem directly before solving the problem. Another one is “The Meaning of Life”. The book records the experience of people in the concentration camp, who were naked and lost all their identities and property, on the most basic life and death line. He wrote that no matter what environment a person is in, he actually has the freedom to choose his own behavior and will.&lt;/p&gt;
&lt;p&gt;During this period without my own time, I also experienced several emotional ups and downs. At first, I was not used to the side effects of my previous work, because for a long time, I would feel a mixture of shame, guilt and fear when I was not working. Later, it came from the discomfort of not being able to realize myself, and the discomfort of wanting to do some work but not having the conditions; now I try to accept the status quo, be calm, and accept that life is originally a process from nothing to nothing. In fact, the meaning is given by oneself. Work is meaningful, and taking care of children is actually meaningful. The time of companionship is actually decreasing day by day. Do a good job of accompanying every day, which is much better than meaningless work and internal consumption.&lt;/p&gt;
&lt;p&gt;I am in pain and have no time, but I also accept the value of the time I spend taking care of children now. Maybe when the child is older and goes to kindergarten, I will have more personal time.&lt;/p&gt;
&lt;p&gt;If I summarize it in one sentence, I think the current stage is: Accept The Status Quo, Know Myself, And Love Life.&lt;/p&gt;</content:encoded></item><item><title>Deploy SiYuan Notes on Synology NAS + External HTTPS Access via Cloudflare Tunnel</title><link>https://daolanx.me/en/posts/post-3/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-3/</guid><pubDate>Tue, 29 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;1-why-choose-siyuan-notes&quot;&gt;1. Why Choose SiYuan Notes&lt;/h2&gt;
&lt;p&gt;I previously used an online note-taking app that worked fine, and I could tolerate its slow feature updates. However, when I recently wanted to export my notes, I discovered there was no bulk export function. Despite numerous user requests for this feature, they didn’t respond - even paid users didn’t have access to it. Instead, third-party tools emerged to support this… This reveals some issues with their product philosophy and vision. Product stickiness should be achieved through better experiences and features, not by trapping users with high migration costs due to accumulated data. That’s going too far. For long-term considerations, after exporting my notes using third-party tools, I began looking for alternatives.&lt;/p&gt;
&lt;p&gt;I wanted a note-taking product with two core features: data freedom/security and a WYSIWYG Markdown experience. Notion offers a good experience, but with data and servers overseas, access speed is an issue even with data syncing. Obsidian is popular and meets basic requirements with a rich plugin ecosystem, but I’m not comfortable using apps - I prefer browsers. Plus, its numerous tutorials are overwhelming. I want to take notes, not tinker with the note-taking system itself. I need something that works out of the box.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://b3log.org/siyuan/&quot;&gt;SiYuan Notes&lt;/a&gt; seems decent. It supports NAS docker deployment and import/export, ensuring data security and freedom. The experience is WYSIWYG, it’s open source with an active repository and decent number of plugins. Since I mainly use docker to deploy web services, the inherent client-server architecture eliminates sync costs and complications.
The SiYuan author has an interesting story - &lt;a href=&quot;https://github.com/88250&quot;&gt;b3logos&lt;/a&gt; has been contributing to open source in this field for 10+ years. While he has some controversial history, he has &lt;a href=&quot;https://github.com/siyuan-note/siyuan/issues/5721&quot;&gt;publicly apologized&lt;/a&gt;. Beyond his passion and capability, I admire his sincerity and courage.&lt;/p&gt;
&lt;h2 id=&quot;2-why-use-cloudflare-tunnel-for-internal-network-penetration&quot;&gt;2. Why Use Cloudflare Tunnel for Internal Network Penetration&lt;/h2&gt;
&lt;p&gt;In short, it’s possibly the simplest and free solution for achieving internal network penetration with custom domain HTTPS access when you don’t have a public IP.&lt;/p&gt;
&lt;p&gt;With a public IP, DDNS would be the simplest and most stable solution. Since I currently can’t get a public IP, I tried some internal network penetration solutions: Oray is well-known with plugins for both routers and NAS, but requires real-name authentication and charges for custom domain resolution. cpolar worked but requires a paid professional version for custom domains and manual HTTPS certificate setup.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloudflare.com&quot;&gt;Cloudflare&lt;/a&gt; truly lives up to its reputation as a cyber bodhisattva, supporting custom domains, automatic HTTPS certificate configuration, and completely free. The only drawback is overseas servers affecting access speed. I tried some IP optimization methods for acceleration but wasn’t successful. The plan is to get the note service running first, then try to obtain a public IP later to switch to DDNS, avoiding the speed and cost issues of data transit in internal network penetration.&lt;/p&gt;
&lt;h2 id=&quot;3-how-to-deploy-siyuan-notes-on-synology-nas-docker&quot;&gt;3. How to Deploy SiYuan Notes on Synology NAS Docker&lt;/h2&gt;
&lt;p&gt;I’m using a Synology DS220+ which supports Docker.&lt;/p&gt;
&lt;h3 id=&quot;31-configure-siyuan-folders&quot;&gt;3.1 Configure SiYuan Folders&lt;/h3&gt;
&lt;p&gt;Use File Station package to create siyuan/workspace folders under the docker folder.&lt;/p&gt;
&lt;p&gt;Right-click siyuan and workspace folders, set Everyone read/write permissions, and apply to subfolders. It’s more convenient to set read/write and inheritance permissions directly on the top-level docker folder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/3/1-1.webp&quot; alt=&quot;1-1&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;32-select-siyuan-docker-image&quot;&gt;3.2 Select SiYuan Docker Image&lt;/h3&gt;
&lt;p&gt;Search and install Container Manager package in Package Center. Under this package, download the SiYuan image b3log/siyuan.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/3/1-2.webp&quot; alt=&quot;1-2&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;33-launch-siyuan-container&quot;&gt;3.3 Launch SiYuan Container&lt;/h3&gt;
&lt;p&gt;Due to persistent submission errors with the execution command input field in the Container Manager interface used for installation, I will switch to the command line for better clarity. Execute the following under the ssh admin account. Note that the ‘xxx’ in accessAuthCode must be replaced with your custom authorization code, which will be used as the Siyuan login password:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt; # switch to root account&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# install siyuan, [ref](https://github.com/siyuan-note/siyuan)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; docker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; siyuan&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /volume1/docker/siyuan:/siyuan&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 6806:6806&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -u&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; root&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; b3log/siyuan:latest&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -workspace&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /siyuan&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --accessAuthCode=xxx&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;34-internal-network-testing&quot;&gt;3.4 Internal Network Testing&lt;/h3&gt;
&lt;p&gt;Access via browser using synology_ip:6806, with the password being the accessAuthCode set during installation.
&lt;img src=&quot;/images/posts/3/1-4.webp&quot; alt=&quot;1-4&quot;&gt;&lt;br&gt;
The interface language can be changed to English through settings.
&lt;img src=&quot;/images/posts/3/1-5.webp&quot; alt=&quot;1-5&quot;&gt;&lt;/p&gt;
&lt;p&gt;This completes the installation of SiYuan Notes on the Synology internal network.&lt;/p&gt;
&lt;h3 id=&quot;35-some-issues-and-solutions-when-starting-the-container&quot;&gt;3.5 Some Issues and Solutions When Starting the Container&lt;/h3&gt;
&lt;h4 id=&quot;351-no-data-in-container-manager-image-list&quot;&gt;3.5.1 No Data in Container Manager Image List&lt;/h4&gt;
&lt;p&gt;If there are issues with the image source list and downloads, try the Beta version first. I used version 24.0.2-1525, which fixed some problems with retrieving and downloading image sources.&lt;/p&gt;
&lt;h4 id=&quot;352-startup-failure-addgroup-permission-denied&quot;&gt;3.5.2 Startup Failure: addgroup: permission denied&lt;/h4&gt;
&lt;p&gt;After executing the above docker run command, installation failed with the error:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Creating&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; group&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; siyuan&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (1000)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addgroup:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; permission&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; denied&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (are &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;you&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; root?&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tried switching to root account with sudo -i, but the error persisted. After investigation, found that Entware needs to be installed first to create the group and user.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Install Entware by following [Entware guide](https://github.com/Entware/Entware/wiki/Install-on-Synology-NAS)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;wget&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -O&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://bin.entware.net/x64-k3.2/installer/generic.sh&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; /bin/sh&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Update package sources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;opkg&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Install shadow-utils&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;opkg&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; shadow-utils&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Create group and user&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; groupadd&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1000&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; siyuan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; useradd&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -u&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1000&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; siyuan&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; siyuan&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;4-how-to-configure-cloudflare-tunnel&quot;&gt;4. How to Configure Cloudflare Tunnel&lt;/h2&gt;
&lt;h3 id=&quot;41-host-domain-on-cloudflare&quot;&gt;4.1 Host Domain on Cloudflare&lt;/h3&gt;
&lt;p&gt;You can create a new domain on Cloudflare, or &lt;a href=&quot;https://juejin.cn/post/7267733291569168423&quot;&gt;transfer an existing domain to Cloudflare&lt;/a&gt;. Wait until the domain status shows as active in the domain management menu.
&lt;img src=&quot;/images/posts/3/2-15.webp&quot; alt=&quot;2-15&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;42-create-new-tunnel-on-cloudflare-website&quot;&gt;4.2 Create New Tunnel on Cloudflare Website&lt;/h3&gt;
&lt;p&gt;Go to the tunnels page under Cloudflare Zero Trust and click to create a new tunnel&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/3/2-1.webp&quot; alt=&quot;2-1&quot;&gt;
&lt;img src=&quot;/images/posts/3/2-2.webp&quot; alt=&quot;2-2&quot;&gt;
&lt;img src=&quot;/images/posts/3/2-3.webp&quot; alt=&quot;2-3&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can enter any name for the tunnel here
&lt;img src=&quot;/images/posts/3/2-4.webp&quot; alt=&quot;2-4&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here Cloudflare provides us with the token for the current tunnel, and indicates that we need to install the environment client next.
&lt;img src=&quot;/images/posts/3/2-5.webp&quot; alt=&quot;2-5&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;43-install-cloudflare-client-on-nas&quot;&gt;4.3 Install Cloudflare Client on NAS&lt;/h3&gt;
&lt;p&gt;There are two installation methods to choose from.&lt;/p&gt;
&lt;h4 id=&quot;431-simplest-installation-method---using-package-installation&quot;&gt;4.3.1 Simplest Installation Method - Using Package Installation&lt;/h4&gt;
&lt;p&gt;The simplest way is to add &lt;a href=&quot;https://spk7.imnks.com&quot;&gt;IMNKS Synology&lt;/a&gt; as a new package source in Package Center, then install Cloudflare tunnel from the community packages and enter the token provided on the website. I encountered an issue where this image source was not very stable, sometimes the package list would not display.
&lt;img src=&quot;/images/posts/3/2-6.webp&quot; alt=&quot;2-6&quot;&gt;
&lt;img src=&quot;/images/posts/3/2-7.webp&quot; alt=&quot;2-7&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;432-more-stable-installation-method---using-docker&quot;&gt;4.3.2 More Stable Installation Method - Using Docker&lt;/h4&gt;
&lt;p&gt;Download the cloudflare/cloudflared image in Container Manager&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/3/2-8.webp&quot; alt=&quot;2-8&quot;&gt;&lt;/p&gt;
&lt;p&gt;For command line installation, we add -d —name=cloudflare —restart unless-stopped to the official command to set the container name, run in background, and auto-restart unless manually stopped. Replace your-token with the token provided on the official website earlier.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --name=cloudflare&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --restart&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; unless-stopped&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; cloudflare/cloudflared:latest&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tunnel&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --no-autoupdate&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --token&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;your-toke&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After Docker is running, refresh the tunnels page on the Cloudflare website. You should see the tunnel status is normal. Click on the created tunnel to continue configuring the Public Hostname.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/3/2-9.webp&quot; alt=&quot;2-9&quot;&gt;
&lt;img src=&quot;/images/posts/3/2-10.webp&quot; alt=&quot;2-10&quot;&gt;
Here you configure the external domain name and subdomain for access, along with the internal HTTP access method for SiYuan. For example, using note.xyz.com for external access, where 192.168.x.x:6806 is the internal IP and port for accessing SiYuan.
&lt;img src=&quot;/images/posts/3/2-11.webp&quot; alt=&quot;2-11&quot;&gt;
After successful configuration, you’ll see a new CNAME record added to the hosted domain’s DNS.
&lt;img src=&quot;/images/posts/3/2-13.webp&quot; alt=&quot;2-13&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;433-testing-external-access&quot;&gt;4.3.3 Testing External Access&lt;/h4&gt;
&lt;p&gt;You can now access your NAS-deployed SiYuan document via HTTPS using the domain name we just configured. With this, the installation is complete.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/3/2-12.webp&quot; alt=&quot;2-12&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-reflections&quot;&gt;5. Reflections&lt;/h2&gt;
&lt;p&gt;Unlike companies that mandate specific products/technologies, good technology and product ecosystems aren’t about having just one standard answer or zero-sum games. Products and technologies should be like shopping at a supermarket - each with their own characteristics, offering choices, freedom to select based on objective and subjective conditions, and flexibility in combinations.&lt;/p&gt;
&lt;h2 id=&quot;6-references&quot;&gt;6. References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.itdog.net/post/608.html&quot;&gt;Installing SiYuan Note Server Using Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hackfang.me/nas-cloudflare-tunnel&quot;&gt;Tutorial on Using Cloudflare Tunnel for Internal Network Penetration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>ESP32 and AI-Powered Personalized Calendar with E-ink Display</title><link>https://daolanx.me/en/posts/post-2/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-2/</guid><pubDate>Mon, 14 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;This is an ESP32-based e-ink AI calendar. Like time itself - yesterday cannot be kept, today is fresh, and tomorrow remains unknown - it displays unique AI-generated content each day that cannot be saved or modified, running silently with ultra-low power consumption and no power switch.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/daolanx/eink-ai-calendar&quot;&gt;https://github.com/daolanx/eink-ai-calendar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div style=&quot;display: flex; flex-direction: row; gap: 10px;&quot;&gt;
  &lt;div&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/2/ai-calendar-front.jpg&quot; alt=&quot;ai-calendar-front&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/2/ai-calendar-back.jpg&quot; alt=&quot;ai-calendar-back&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/2/ai-calendar-screenshots.png&quot; alt=&quot;ai-calendar-screenshots&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/2/ai-calendar-componets.jpg&quot; alt=&quot;ai-calendar-components&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;origin&quot;&gt;Origin&lt;/h1&gt;
&lt;p&gt;It all started when I came across LiangGao’s article &lt;a href=&quot;https://niu.sspai.com/post/82704&quot;&gt;“I Created an Electronic Calendar in the Digital Age to Frame Oil Paintings and Photos”&lt;/a&gt;. I was amazed by the results and wanted something similar to remind myself of time’s unique nature - how it can’t be retrieved or predicted, yet is so easily forgotten.&lt;/p&gt;
&lt;p&gt;Initially, I wanted an electronic calendar that could display photos from my online albums, preferably randomly pulling from my NAS storage. However, after searching through shopping apps, I found that most products either stored photos locally (I later understood why) or had only black and white or tri-color displays. The available options were either pure photo frames or calendars cramped with information. The product I envisioned simply didn’t exist in the market.&lt;/p&gt;
&lt;p&gt;So I decided to build it myself. Through the comments section of the article mentioned above, I found &lt;a href=&quot;https://github.com/Ymriri&quot;&gt;Ymriri’s&lt;/a&gt; implementation of &lt;a href=&quot;https://github.com/Ymriri/esp32_7color&quot;&gt;esp32_7color&lt;/a&gt;. I really admired their enthusiasm and hands-on ability to transform a front-end and back-end separated e-commerce management platform into a photo management system. However, the code was quite messy. The project documentation even acknowledged this with a note saying “I know what you want to say, the folder structure is too messy, I know, but I’m too lazy to fix it, you can fix it yourself.” After several attempts to understand the code, I gave up.&lt;/p&gt;
&lt;p&gt;Finally, I found &lt;a href=&quot;https://github.com/Debatrix&quot;&gt;Debatrix’s&lt;/a&gt; &lt;a href=&quot;https://github.com/Debatrix/eink-calendar&quot;&gt;eink-calendar&lt;/a&gt;. Their code was the simplest, and with Cursor + GPT as programming companions, I dared to tackle unfamiliar Python and C code, learning and modifying as I went along.&lt;/p&gt;
&lt;h1 id=&quot;from-nas-photo-calendar-to-ai-image-text-calendar&quot;&gt;From NAS Photo Calendar to AI Image-Text Calendar&lt;/h1&gt;
&lt;p&gt;Initially, I tried to find Synology APIs to read photo data, but this proved complicated. While working on it, I had an inspiration - AI-generated images combined with daily information would better capture the essence of a calendar. So I started looking for AI text-to-image APIs.&lt;/p&gt;
&lt;p&gt;I first tried &lt;a href=&quot;https://developers.cloudflare.com/workers-ai/&quot;&gt;Cloudflare’s workers-ai&lt;/a&gt;. While it worked, the network calls were too slow. Thinking domestic APIs might be faster, I switched to Alibaba Cloud’s AI SDK, which indeed improved performance.&lt;/p&gt;
&lt;h1 id=&quot;why-client-server-architecture&quot;&gt;Why Client-Server Architecture&lt;/h1&gt;
&lt;p&gt;Looking at LiangGao and Debatrix’s code, both used client-server architecture. During development, it becomes clear that C/S architecture is more complicated - server deployment is a challenge. I suspect this is why many commercial photo frames use local storage. From a maintenance cost and privacy perspective, local storage makes more sense, and manufacturers likely made this product decision early on.&lt;/p&gt;
&lt;p&gt;I initially thought about processing everything on the ESP32 - fetching original images, handling color dithering and data processing. However, this proved impractical, likely due to computing power and storage limitations. With limited resources on this topic, I stuck with the C/S architecture.&lt;/p&gt;
&lt;p&gt;For server deployment, I first considered serverless computing as it seemed ideal for this use case. I tried &lt;a href=&quot;https://developers.cloudflare.com/workers/&quot;&gt;Cloudflare Workers&lt;/a&gt;, but it didn’t support necessary Python libraries like &lt;code&gt;PIL&lt;/code&gt;. I looked into Alibaba Cloud’s lightweight ECS and serverless options but was deterred by documentation and pricing. Considered using a Raspberry Pi, but didn’t want another device to power. Finally, since I had a Synology NAS supporting Docker at home, deploying Docker images there proved secure and convenient.&lt;/p&gt;
&lt;h1 id=&quot;cleaner-ui&quot;&gt;Cleaner UI&lt;/h1&gt;
&lt;p&gt;Since LiangGao replaced todo information with quotes, the UI needed modification. Looking at similar open-source calendars, many seemed cluttered with information, perhaps due to programmers’ tendency to maximize data density. Shopping apps didn’t offer many good calendar layouts either - some looked nice but had scattered information. Without finding a better layout, I focused on reducing information and grouping related items for simplicity. Some developers would redesign in Figma, but I haven’t learned that yet.&lt;/p&gt;
&lt;h1 id=&quot;streamlined-functionality&quot;&gt;Streamlined Functionality&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Debatrix&quot;&gt;Debatrix’s&lt;/a&gt; &lt;a href=&quot;https://github.com/Debatrix/eink-calendar&quot;&gt;eink-calendar&lt;/a&gt; includes many features like image storage, hash checking, switch control, and LED control. I initially wanted to add refresh control, but lacking circuit design experience and wanting to avoid complicating the exterior design, I removed switches and LEDs.&lt;/p&gt;
&lt;p&gt;For daily refresh implementation, while an RTC clock module could work, it would add complexity. I discovered that besides wake-up mechanisms, WiFi connection provides time information. So I implemented hourly wake-ups - when WiFi connects and it’s midnight, the display updates, achieving daily refresh without depending on RTC or server timing.&lt;/p&gt;
&lt;h1 id=&quot;enhanced-aesthetics&quot;&gt;Enhanced Aesthetics&lt;/h1&gt;
&lt;p&gt;I started with LiangGao’s 3D-printed model but struggled to remove ESP32 pins with a soldering iron. Plastic or building block cases didn’t match my vision, so I bought a wooden frame. The original backplate was too thick, so I purchased a thinner one. Using calipers to measure internal dimensions helped find perfectly fitting batteries and chips.&lt;/p&gt;
&lt;p&gt;Connecting the backplate to the frame was another challenge. Initially considered a diary-style latch but couldn’t find a suitable one and didn’t want a protruding clasp. Magnetic attachment seemed promising, but strong magnets risked unsticking. I designed and 3D printed magnet boxes to reduce magnetic force and increase adhesion area.&lt;/p&gt;
&lt;p&gt;Current magnetic force is slightly weak - ideally, the back cover should have magnets with magnet boxes on the frame for optimal strength.&lt;/p&gt;
&lt;p&gt;Though inexperienced with 3D modeling, I learned through GPT guidance - creating rectangles and using boolean difference operations to make masks and magnet boxes.&lt;/p&gt;
&lt;h1 id=&quot;development-insights&quot;&gt;Development Insights&lt;/h1&gt;
&lt;p&gt;Honestly, this period was an exploration during technical uncertainty. Previously, I felt resistant to and unfamiliar with technology. Inspired by talks from pioneers like &lt;a href=&quot;https://www.youtube.com/watch?v=MKXjjpZqZwU&quot;&gt;Apple’s Wozniak&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/watch?v=o8NPllzkFhE&quot;&gt;Linux’s Torvalds&lt;/a&gt;, I realized technology is meant for play and creating value, not just for exams and work. Their achievements weren’t from hard work alone - Wozniak grew up playing with math and circuits during Silicon Valley’s rise, while Torvalds created Linux for fun and Git for Linux maintenance. I regret not experiencing Silicon Valley’s rapid development era and misunderstanding technology through university and work experiences.&lt;/p&gt;
&lt;p&gt;Returning to basic technical understanding, here’s what I learned:&lt;/p&gt;
&lt;h3 id=&quot;1-maintain-courage-and-curiosity&quot;&gt;1. Maintain Courage and Curiosity&lt;/h3&gt;
&lt;p&gt;Previously, I was reluctant to tackle unfamiliar tech stacks. With Cursor and GPT, I could better understand code and try new things. While AI may impact programmers, it also creates opportunities. Though unfamiliar with ESP32 and Python, AI assistance helped me debug and modify, accessing interesting projects. However, AI still has limitations and lacks comprehensive understanding. As a programming assistant, it’s like a prep cook to a chef or an EVA pilot to a mech - working together enhances potential.&lt;/p&gt;
&lt;p&gt;Facing difficulties reminds me of playing Black Myth: Wukong - seemingly impossible bosses become manageable with practice and the right mindset. Courage to try matters more than methods.&lt;/p&gt;
&lt;p&gt;As &lt;a href=&quot;https://www.youtube.com/watch?v=wHGqp8lz36c&quot;&gt;J.K. Rowling&lt;/a&gt; said, you’ll only avoid failure by living so cautiously that you might as well not have lived at all - that’s the ultimate failure.&lt;/p&gt;
&lt;h3 id=&quot;2-investigation-leads-to-knowledge&quot;&gt;2. Investigation Leads to Knowledge&lt;/h3&gt;
&lt;p&gt;Technology is a means, not an end. Learning through application is most effective. Discovering problems and goals while integrating resources and methods to solve them provides better learning than just studying for answers. This process develops both learning methods and problem-solving mindset.&lt;/p&gt;
&lt;p&gt;Though initially unfamiliar with Python, ESP32, and e-ink displays, I found similarities in web processing, basic program logic, and endpoint handling. This demonstrates the interconnected nature of computer science fundamentals.&lt;/p&gt;
&lt;p&gt;Many problems have multiple solutions - there’s rarely one correct answer. Choices depend on current judgment and trade-offs.&lt;/p&gt;
&lt;h3 id=&quot;3-completion-over-perfection&quot;&gt;3. Completion Over Perfection&lt;/h3&gt;
&lt;p&gt;Many say this, but experience brings understanding. I often felt urged to refactor - using Python’s Flask or even Node.js - but reason prevailed. Pursuing perfection could prevent completion. Focus on goals, complete functionality, maintain restraint and clarity.&lt;/p&gt;
&lt;h3 id=&quot;4-joy-in-sharing&quot;&gt;4. Joy in Sharing&lt;/h3&gt;
&lt;p&gt;Wozniak said technology is about doing good things and sharing. I hope I’ve achieved that here.&lt;/p&gt;
&lt;h1 id=&quot;acknowledgments&quot;&gt;Acknowledgments&lt;/h1&gt;
&lt;p&gt;Thanks to open source for connecting us with helpful people and interesting projects;
Thanks to AI for making unfamiliar technology more accessible.&lt;/p&gt;</content:encoded></item><item><title>Farewell Ant, Boundless Sea And Sky</title><link>https://daolanx.me/en/posts/post-1/</link><guid isPermaLink="true">https://daolanx.me/en/posts/post-1/</guid><pubDate>Fri, 14 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In June 2024, I left Ant Group, exactly ten years after joining in June 2014. Prior to that, I worked for three years in the Taobao Kariqu project team from March 2011 to June 2024, adding up to thirteen years at Alibaba.&lt;/p&gt;
&lt;p&gt;After my internship at Baidu’s commercial products division, my first formal job was at NetEase in Beijing, working on e-commerce projects. Back then, I received a call from Xiao Ma, asking if I was interested in opportunities at Taobao in Hangzhou, where they had the “best UED on the planet.” As a newbie, I couldn’t resist such a vision and felt like I was on a pilgrimage to Hangzhou for the interview. During those days, Hangzhou was covered in heavy snow, adding a sense of ceremony to the experience. Due to my lack of experience, I didn’t pass the interview for Taobao’s front-end team. However, during interviews with other departments, I thought I would return empty-handed, but coincidentally, Taobao was working on an innovative project called Kariqu, which was hiring front-end developers. I remember my second interview was with Teacher Yun Qian, who was very humble, and the project leader was full of energy. Thanks to their trust, I was fortunate to pass the interview.&lt;/p&gt;
&lt;h2 id=&quot;the-kariqu-project-team-the-grassroots-entrepreneurial-spirit-i-never-saw-again&quot;&gt;The Kariqu Project Team: The Grassroots Entrepreneurial Spirit I Never Saw Again&lt;/h2&gt;
&lt;p&gt;Kariqu is the source of the Yellow River, and the project team was named after it, which also related to their focus on internet traffic, a time when traffic portals were king. The team worked on products like Tao URL, Tao Video, and Mobile Manager, some of which ranked among the top in China.&lt;/p&gt;
&lt;p&gt;More than the products, what left a deeper impression was the team. We worked in a small, cramped room with product managers, operations, developers, designers, and testers, all collaborating on products, analyzing data, and reviewing feedback. I was honored when Ma Yun visited, showing great interest in our products. The team leader communicated brilliantly with him, presenting data metrics flawlessly, clearly well-prepared.&lt;/p&gt;
&lt;p&gt;While the products grew, our promotions were less optimistic. Due to professional evaluations, we often failed to get promoted, leading to sighs and complaints about the judges not understanding the products. The architect even joked that we were “mud-legged.” Despite the jokes, the team had a harmonious relationship, and the rapid development of the internet filled everyone with confidence, hope, and tolerance.&lt;/p&gt;
&lt;p&gt;Looking back, although I didn’t grow much technically in the Kariqu team, the most valuable takeaway was that grassroots entrepreneurial spirit. In later teams, even if they were larger and more professional, I never saw that same passion for products and users again.&lt;/p&gt;
&lt;p&gt;Later, due to departmental changes, a new design lead was brought in. I felt they weren’t up to par, and I was asked to create demos—basically spending a week on something the boss would glance at for a few seconds. I wanted to work on real products, so when Alipay was hiring front-end developers, I decided to transfer. Thanks to Yu Bo’s trust, I joined the Experience Technology Department.&lt;/p&gt;
&lt;h2 id=&quot;ant-experience-technology-the-palace-of-academics&quot;&gt;Ant Experience Technology, The Palace Of Academics&lt;/h2&gt;
&lt;p&gt;At that time, I joined the Basic Products Department, mainly website-related business. At that time, Su Qian and the others promoted the transformation of Node.js on the Alipay homepage and were fortunate to join in the development. At that time, I was going to release the official website and found that I had to go to 30+ small platforms to complete the development and release. They were all shocked. Later, I planned front-end engineering transformation, and based on my trust, I became the main development of the front-end R&amp;#x26;D of Swift, which became Ant’s earliest front-end R&amp;#x26;D platform. At that time, our first and second phases were quite effective and we won many awards.&lt;/p&gt;
&lt;p&gt;I felt very awesome at the time, but now I think it is actually like this. Maybe at that stage, the Internet was developing rapidly and had strong demands for related products. The strong demand for products and slow R&amp;#x26;D efficiency became the main contradiction, and Ant was originally Mainly finance and Java, the R&amp;#x26;D efficiency of the front-end is obviously insufficient, and there is room for existence, which constitutes the basic space and value of the front-end R&amp;#x26;D platform. Then in terms of team structure, based on Zhen’er’s understanding of the team, his colleagues are very diverse. Although everyone is on the front-end title, there are actually big differences. Some of them can write Java, chat about Kafka and election algorithms every day. DDD and congestion models, some use go to write docker and build services, and some write more publicity, which overall constitutes a relatively comprehensive team capability, which is also because of management belief and let go. Although everyone has many different abilities, they have cooperation. It can really complement each other and pursue simplicity in the ultimate pursuit, so there is a good push in product development and execution, and it is pragmatic and simple. The quality of product development has been guaranteed, and the design has also changed the style of Ant Financial’s multiple card points and multiple points. It has learned from Kenya Hara’s minimalist design, and has tried its best to provide services in a scenario-based manner, and has a simple and stable service to developers. It will do it during that period A sense of difference. At that time, Swift’s architectural technology and promotion also received a lot of support. Although I was responsible for the main front-end development during that period, I now think of Frodo, who looks more like myself and the Lord of the Rings, actually wizards, elves, and dwarves on the way. He is amazing. Although he finally escorted the Lord of the Rings, it would be impossible to complete without their help.&lt;/p&gt;
&lt;p&gt;There is also a layer of trend interpretation and vision from Zhen’er. One is the interpretation of trend devops. It is in line with the development trend, and the other is the vision. At that time, she felt that Ant front-end engineers were originally very aura and creative, and she had seen many people come to Alipay after seeing her. It was all gone. She hoped to build a platform to liberate and exert the creativity of front-end engineers. The platform at that time could also allow front-end students to create and call many BAAS services. Although it seemed a bit radical at the time, this design was really full of Highlights. Many years later, I looked at Vercel’s familiar design and felt complicated.&lt;/p&gt;
&lt;p&gt;At that time, I was also lucky to work with many big guys and listen to and share. For example, Mr. Ruan Yifeng also briefly participated in the project development, and there were also migrant workers promoting rxjs, Zhou Aimin spoke js language, and there were many Su Qian, Bu Si, Guan Gao, Shen Yu, Ping Right, Yun Qian and other great people. Please tell me why Gao P can insist on writing code. The answer is very simple. I think it is valuable and continue to do it. Please tell me how to make a good design architecture. The answer is mainly to pay attention to code quality, testing, and cr. The free plan asked him questions in exchange for warm thanks for your support. Really humble to the core. There are also some visual research that originally knows nothing about SQL. Based on their research attitude towards technology, they can still complete the design of the SQL engine in a short period of time, which is really different. I think it is obvious trait of a big guy to maintain humility and study, and to pursue technology endlessly.&lt;/p&gt;
&lt;p&gt;The financial core support in the later period. Although the support range is relatively wide, it seems that it is still academic. One is its own support method, or the awareness of experience technology to support the product front-end. The thinking and method are still the same trick of experience technology. , and did not fully interpret the product to do things that are more beneficial to the product. Most of them are still paying attention to the implementation and transformation of technical solutions. One is that the product itself may not be a real scenario, especially some technical middle platforms, which may be difficult to capture real demands, which is different from the feeling of commercial products outside.&lt;/p&gt;
&lt;p&gt;There is something I remember deeply. At the dinner table, the head of the financial core R&amp;#x26;D joked that you were relatively simple in front of you. I was not convinced at the time, what was simple in that way? Now I think about it hahaha he was right. I always want to treat business as a technology implementation scenario, but it is all about scholarly spirit.&lt;/p&gt;
&lt;p&gt;Later, OceanBase is about to establish an independent subsidiary. At that time, it happened to be the core of finance. Because of the method and the result, I performed 325 (the reason was that others were developing rapidly, and I was standing still, and standing still was regressing). Yushu asked me if I was willing to go. OceanBase, there needs to be full-time participation and support, thanks for trust. After thinking for a few days, I joined OceanBase and worked hard to promote new business directions.&lt;/p&gt;
&lt;h2 id=&quot;oceanbase-my-graduation-ceremony&quot;&gt;OceanBase, My Graduation Ceremony&lt;/h2&gt;
&lt;p&gt;In the first year, it was really like a wisp of thin ice. In June 2020, OceanBase established an independent company and its products began to develop rapidly. On the one hand, we are happy with the development of the business, on the other hand, at this time we only have 2 front-end + 3 outsourcing, and the front-end performance is facing great challenges. So like a knight, I participated in the development of various urgently needed front-end products according to my priorities, while paying attention to recruitment and teams. Finally, with the help of myself and the experience technology team, I completed the front-end formation of 5 front-end + 14 outsourcing. At that time, the supervisor’s evaluation was based on a sense of responsibility and an overall view. Yes, in fact, many things may actually be inconsistent with personal interests, but I am willing to choose to pay.&lt;/p&gt;
&lt;p&gt;Later, with the development of the cloud business, I just stayed in the cloud business, from project governance, to product flexibility, to scale, bit by bit to assist in product formation and development. It also put a lot of time and energy into the formation of the front-end 10+ team of Numa.&lt;/p&gt;
&lt;p&gt;The technical solution was mainly the formation of the database console business component library. At that time, the environment was faced with scattered requirements, PD was often changed, the back-end interface was inconsistent, and the front-end console had been repeated many sets. My interpretation is that demand is a kind of resource, and scenarios are also a kind of resource. If it is not used, it will become a low cost, but rational use is the benefit. A large number of needs can definitely converge to some capabilities and solutions. Multiple scenarios can just do the plan polishing, stable and consistent components can also reduce project costs, forThe subsequent back-end interface provides a consistent reference and provides some support for subsequent open source. So although it looks mediocre, in fact, I have already changed. Instead of setting a template or making a plan for promotion, I am thinking about how to make more effective solutions, solve practical problems, and find the best solution for myself and the surrounding teams. Is there a solution that can solve product problems, but also conducive to the development of the cooperative team, and also conducive to the improvement of the technical team’s own professionalism? Technology is not single. It must play a role in a time and space. It is necessary to see that it is in the development space of people and things to find the optimal solution.&lt;/p&gt;
&lt;p&gt;In fact, you will also find in the process that although the database and console are very unfamiliar scenarios, the ToB scenario has its own rules, and the requirements will teach you. At that time, in the console field, it can be basically adapted to different product specifications, and there are corresponding white-screen solutions for operation and maintenance, monitoring, and diagnosis.Although it is not refined enough, there are scenes with scale, and if the plan is polished, I think it will still make the professionalism of this scene. In the process, you can even feel the law and joy of the development and evolution of the ToB functional product tree, a kind of joy of realization in the sense of regularity and thorough interpretation.&lt;/p&gt;
&lt;p&gt;Doing technology in the product team, the most important thing is that I think it is result-oriented effectiveness. At that time, a startup CTO happened to listen to the front-end sharing of a major manufacturer. He said, everyone sitting here is a front-end bull. I really benefited a lot from listening to it, but I’m also honest. If a startup uses your plan, it may die and it will die quickly. Indeed, I also checked some of the company’s internal technologies later. Many of them I think are very promising, but many may be solving some internal problems. The market and commercialization may not be flexible. Of course, it’s not that I don’t pay attention to technology. The example I am very impressed with comes from the early days of Apple Computers. Apple computer users never looked at the inside, but in fact, their internal cable design is very neat, and they got up early and signed the engineer’s name. I believe that technology is still a service method rather than the purpose itself, and products and services are important. However, the technology behind good products and services must not be bad. Technology will be more professional and extreme because of the services provided. In the commercialization period, we are actually thinking about many technologies within ants. We are also thinking about whether they are really suitable for commercial products, because the main genes at the front end of ants are also in the back end, and the commercialization technology in the market has long been different. However, based on development and dependence, this promotion will be relatively slow, and this proposition may require more and more attention.&lt;/p&gt;
&lt;p&gt;Just as many movies have ups and downs, art also comes from life. It is difficult for people to change their environment many times, and they don’t have to reason with unreasonable people. So in the choice between leaving and staying as an IC, I chose to leave. Things in the world are inherently complicated, and many things cannot be measured by fairness, and the operation of the world is not transferred by individuals. When you understand this, you may be more mature.&lt;/p&gt;
&lt;p&gt;Almost everyone was surprised when I left and didn’t believe it, but that’s it. Someone asked if it was a pity to leave after more than ten years of work, but I don’t think so. The ant in my heart is still the ant that has brought small and beautiful changes to the world, and the pragmatic and simple ant in my heart. If an environment is not like this anymore, then it may not be an ant. I think my departure is actually a kind of return. Some people say that your management was actually very good before, but I said that now you don’t think it’s a matter of management level, it’s driven by responsibility. Now that the burden has finally been removed, and it is light without responsibility.&lt;/p&gt;
&lt;h2 id=&quot;goodbye-10-years-of-ant-3-years-of-taobao-13-years-of-ali&quot;&gt;Goodbye, 10 Years Of Ant, 3 Years Of Taobao, 13 Years Of Ali&lt;/h2&gt;
&lt;p&gt;Later, on the resignation box, it was really this paragraph,&lt;/p&gt;
&lt;p&gt;“Running to the Mountains and Seas”&lt;/p&gt;
&lt;p&gt;“Making a small but beautiful difference in the world”&lt;/p&gt;
&lt;p&gt;I still have the tree species of the Ant Forest at that time, and I also keep the Su Dongpo version of the ant doll. I find them very gracious.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/posts/1/ant-family.jpeg&quot; alt=&quot;ant-family&quot;&gt;&lt;/p&gt;
&lt;p&gt;The end of one story is the beginning of another.&lt;/p&gt;
&lt;p&gt;Goodbye Ant, Goodbye Ali!&lt;/p&gt;
&lt;p&gt;Hello, Future!&lt;/p&gt;</content:encoded></item></channel></rss>