今日已更新 133 条资讯 | 累计 37382 条内容
关于我们

标签:#NeXT

找到 169 篇相关文章

AI 资讯

What actually crosses the React Server Component boundary

Everyone can type "use client" . Almost nobody can say what survives the trip across it — and then something breaks: next build dies at prerender, the error names no file and no import chain, and the prop that killed it was an arrow one level down inside an object called options . Here's the uncomfortable secret: the boundary is one serializer . React walks every prop you hand a client component, encodes each value it has a branch for, and throws on the first one it doesn't. This post reads those branches out of React 19's Flight source — one file, no framework — and shows the two traps that pass code review and fail the build anyway. What crosses A prop is legal if the serializer has a branch for it. Everything else falls into one prototype check and throws. The whole contract fits on a screen: // app/page.tsx — a Server Component. Every comment is the serializer's verdict. export default function Page () { return ( < Chart title = "Q3" data = { { rows : [ 1 , 2 , 3 ] } } when = { new Date () } seen = { new Set ([ 1 ]) } index = { new Map () } rows = { fetchRows () } // an un-awaited Promise; the client calls use(rows) bytes = { new Uint8Array ( 8 ) } // ArrayBuffer, DataView, every typed array upload = { new File ([], ' a.csv ' ) } // there is no File branch — a File is a Blob form = { new FormData () } stream = { new ReadableStream () } kind = { Symbol . for ( ' chart ' ) } // global symbols cross; Symbol('chart') throws Slot = { Legend } // a client component: a function, and a client reference save = { saveRow } // a "use server" function: a server reference err = { new Error ( ' boom ' ) } // crosses — and arrives empty in production // no branch — every one of these throws at render match = { /q3/ } href = { new URL ( ' https://x.dev ' ) } cache = { new WeakMap () } user = { new User ( ' ada ' ) } bare = { Object . create ( null ) } onPick = { ( id ) => select ( id ) } /> ); } Four of those lines are the ones people get wrong: new Error() crosses, and product

2026-07-12 原文 →
AI 资讯

React Compiler in 2026: What It Actually Memoizes (And What It Doesn't)

Headline: React Compiler — formerly React Forget — shipped stable with React 19 and automatically memoizes components, hooks, and callbacks by analyzing data flow at build time. No dependency arrays to write; the compiler infers them. Here is what it handles, when it opts out, and whether you should delete your useMemo calls. Key takeaways React Compiler inserts useMemo , useCallback , and React.memo automatically at build time — no dependency arrays to maintain. Enable it in Next.js 15/16 with experimental.reactCompiler: true in next.config.ts . The compiler is conservative: if it cannot prove memoization is safe, it emits the component unchanged. "use no memo" is the escape hatch for functions the compiler should not touch. Run npx react-compiler-healthcheck@latest before enabling to see coverage and violations. What does React Compiler actually do? React Compiler transforms component and hook code at build time to insert memoization automatically. Instead of useMemo(() => expensiveCalc(a, b), [a, b]) , the compiler analyzes data flow, determines which values are stable across renders, and emits equivalent memoized code. The compiled output uses React's memo infrastructure at runtime. The compiler is babel-plugin-react-compiler — it works with any Babel-based build pipeline. How do I enable it in Next.js? // next.config.ts const nextConfig = { experimental : { reactCompiler : true , }, }; export default nextConfig ; Before enabling, run the healthcheck: npx react-compiler-healthcheck@latest The healthcheck reports optimizable component count, files with violations, and blocking patterns. Fix violations first for more coverage on day one. What does the compiler memoize? Components — equivalent to React.memo ; re-renders only when props change. Values — equivalent to useMemo ; computed results, derived arrays, objects. Callbacks — equivalent to useCallback : event handlers, functions passed as props. Dependencies are inferred from escape analysis — n

2026-07-12 原文 →
AI 资讯

Partial Prerendering in Next.js: The Static Shell + Dynamic Stream Model

Headline: Partial Prerendering (PPR) in Next.js serves a static HTML shell from the CDN edge instantly, then streams Suspense-wrapped dynamic children from the origin in the same HTTP response. No full-page ISR staleness, no full-page origin latency. I shipped it on two production routes — here is the model. Key takeaways PPR serves a static HTML shell from the CDN edge , then streams dynamic Suspense children from the origin in the same response. The static shell is built at build time — outside <Suspense> renders statically; inside renders dynamically per request. PPR replaces the ISR vs. dynamic tradeoff for pages that are mostly static with isolated personalized sections. No changes to Server Components or Suspense — just experimental.ppr: 'incremental' in config and export const experimental_ppr = true per route. PPR and use cache are complementary : CDN delivery for the shell, origin memoization for dynamic islands. What does PPR actually do? PPR splits a page into two rendering phases within the same HTTP response. At build time, Next.js freezes everything that does not read dynamic request data into a static HTML shell on the CDN edge. At request time, the CDN delivers the shell at edge latency while the origin streams each <Suspense> boundary's content into the same response. On a product page: navigation, title, and description arrive at CDN speed. The in-stock badge and personalized recommendations stream from the origin a fraction of a second later. The user sees a nearly-complete page immediately. How is PPR different from ISR and streaming Suspense? Strategy First byte Dynamic freshness Staleness ISR (revalidate: N) CDN edge Whole page up to N seconds stale Full page Dynamic rendering Origin 100% fresh; waits for slowest query None Streaming Suspense (no PPR) Origin Fresh; TTFB includes origin latency None PPR CDN edge Dynamic islands 100% fresh Static shell only How do I enable PPR? // next.config.ts export default { experimental : { ppr : ' inc

2026-07-12 原文 →
AI 资讯

Server Components vs Client Components: The Mental Model Shift Every Vite Developer Needs

Introduction If you have been building applications using Vite, you are likely used to a specific workflow: write React components, bundle them with esbuild/Rollup, and serve a single HTML file that fetches a large JavaScript bundle. In this world, everything is a "Client Component." However, as the React ecosystem shifts toward the App Router and React Server Components (RSC), the architecture is fundamentally changing. For developers moving from a Vite-centric mindset to a Next.js framework, the biggest hurdle isn't the syntax—it's the mental model. In this guide, we will break down the core differences between Server and Client components and how to adapt your Vite-based habits to this new reality. The Vite World: Single-Page Application (SPA) Default In a standard Vite + React project, your entire application lifecycle happens in the browser. The browser requests the page. The server sends a nearly empty index.html . The browser downloads the JS bundle. React hydrates the app, fetches data from an API via useEffect , and renders the UI. While this is excellent for developer experience (DX) and highly interactive dashboards, it often leads to "Layout Shift" and slower "Time to Interactive" for content-heavy pages because the client has to do all the heavy lifting. The Shift: Thinking in "Environment Splits" With React Server Components, the paradigm shifts from "Everything happens on the client" to "Compute where it makes sense." 1. What are Server Components? By default, in the Next.js App Router, every component is a Server Component. These components execute only on the server . They never send their code to the client-side bundle. This allows you to: Access backend resources directly: You can query your database or file system inside the component. Keep secrets safe: API keys and sensitive logic stay on the server. Reduce bundle size: Large dependencies (like a markdown parser or date library) stay on the server and only the resulting HTML is sent to the user

2026-07-10 原文 →
AI 资讯

The Complete Redbelly EligibilitySDK Integration Guide: Widget to Backend to On-Chain

The Redbelly Network EligibilitySDK is the compliance backbone for any dApp that needs to verify user eligibility (KYC, KYB, investor accreditation) before letting a wallet in. The official documentation covers each piece well on its own reference page, but there is no single walkthrough connecting the frontend widget to the backend verifier to the on-chain permission check to a production deployment. This guide is that walkthrough. Everything here was verified against the live documentation at https://docs.redbelly.network/ in July 2026: contract addresses, route names, config fields, issuer DIDs and every error string in the reference section. Every code example was then compiled against the published SDK package (v0.0.31) on React 19 with Vite and on Next.js 16 with the App Router, and the backend verifier was booted and exercised for real. Where the docs and reality diverge (a quickstart repo that is not publicly visible, a credential faucet still under development, three undocumented behaviours the builds surfaced), the guide says so and gives you the workaround. What you will build, in order: A mental model of the two verification mechanisms (and why conflating them costs you a day) A working backend verifier with the three routes the widget demands A plain React integration with full loading and error states A production-grade Next.js App Router setup: secure proxy, SIWE sessions, request gating, and both static and dynamic rendering approaches An end-to-end test run on Redbelly Testnet The decision logic for choosing between the three SDK flows, and the pattern for combining them A complete error reference: every documented error, its cause, and its fix A developer following this guide should have the widget running inside an existing dApp within about four hours. 1. Overview and Architecture What the EligibilitySDK actually is The Redbelly "Onboarding and Eligibility Kit" ( @redbellynetwork/eligibility-sdk ) is a set of React components and hooks for provin

2026-07-09 原文 →
AI 资讯

How I Structure Large Next.js Projects — Folder Architecture Guide

Bad nextjs folder structure does not show up on day one. It shows up at month six when three developers search for the checkout form hook and find four copies. I reorganised a client dashboard after exactly that — this guide is the tree I use now on large App Router projects, why each folder exists, mistakes from my first Next.js apps, and the 10-second findability rule . Real folder tree — production-shaped layout my-app/ ├── app/ # routes only — thin pages │ ├── (marketing)/ # route group — shared layout, no URL segment │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── pricing/page.tsx │ ├── (dashboard)/ │ │ ├── layout.tsx │ │ └── orders/page.tsx │ ├── api/ # route handlers │ │ └── webhooks/stripe/route.ts │ ├── layout.tsx # root layout │ └── globals.css ├── components/ # shared UI — buttons, cards, shell │ ├── ui/ │ └── layout/ ├── features/ # business domains — colocated logic │ ├── auth/ │ │ ├── components/ │ │ ├── hooks/ │ │ └── actions.ts │ └── orders/ │ ├── components/ │ ├── api.ts │ └── types.ts ├── lib/ # server + shared utilities │ ├── db.ts │ └── env.ts ├── hooks/ # truly global client hooks ├── types/ # global TS types ├── data/ # static data, blog posts list └── public/ Routes live in app/ . Business logic lives in features/ . Generic design system pieces live in components/ui . That separation is the whole game. Why each folder exists Folder Purpose Do not put here app/ URLs, layouts, loading.tsx Fat business logic features/ Domain modules (orders, auth) Generic Button components/ui Reusable primitives Order-specific tables lib/ DB clients, env validation React components app/api Webhooks, REST edge cases Every form POST (prefer actions) Thin pages — route files under 40 lines // app/(dashboard)/orders/page.tsx — orchestration only import { OrderTable } from "@/features/orders/components/OrderTable"; import { getOrders } from "@/features/orders/api"; export default async function OrdersPage() { const orders = await getOrders(); return ( <section> <h1>Orders

2026-07-09 原文 →
AI 资讯

How I add semantic search to a Next.js site using Sanity Embeddings

Sanity Embeddings semantic search in Next.js is one of those features that looks complicated from the outside but is surprisingly lean to wire up once you understand the moving parts. This post covers the current native Embeddings feature built into Sanity datasets — not the older Embeddings Index API, which Sanity is sunsetting. If you found a guide that talks about a separate embeddings-index resource you have to provision via the Management API, it is stale; skip it. What Sanity Embeddings actually is Sanity's native Embeddings feature lets you mark document types for vector indexing directly inside your dataset. Sanity handles the embedding model and the vector store; you never manage a separate service. Queries use a dedicated sanity.embeddings.query GROQ function that takes a natural-language string and returns documents ranked by semantic similarity. The feature is available on Growth and Enterprise plans as of mid-2026. The workflow has three parts: Configure which document types get indexed (dataset setting or the Embeddings pane in Sanity Studio). Run a semantic query from your Next.js route handler using the Sanity client. Render the results in a search UI component. Setting up the embeddings index in your dataset Go to Manage → your project → Embeddings (or open the Embeddings pane inside Sanity Studio if your plan surfaces it there). Create an index, give it a name (e.g. site_search ), and select which document types and fields to embed. For a blog you would typically pick post with fields title , excerpt , and body (plain text extracted from Portable Text). Sanity backfills existing documents automatically. New and updated documents are re-embedded on publish via an internal webhook — you do not configure that yourself. There is no code required for the indexing step. The index name you choose here ( site_search ) is what you will pass in the GROQ query. Querying embeddings from a Next.js route handler Create a route handler that accepts a search term,

2026-07-08 原文 →
AI 资讯

I gave an LLM agent write access to my cloud drive. Three bugs taught me how to constrain it.

I wanted a media library that knew the difference between what should exist and what does. Most automation I tried picked one side. Some tools search well and never track what you already have. Others move files and assume the move worked. I wanted the gap between those two things to be the thing the software acted on. So I built Mediary Scout . You name a movie or a show. An LLM agent searches your indexers, transfers the best match into your own cloud drive, then reads the drive back to confirm what landed and what is still missing. It runs self-hosted. You bring your own drive, your own model, your own metadata key. There are desktop builds for Mac and Windows if you just want to double-click and run it, and a read-only demo if you want to watch one acquisition play out first. The drives it speaks today happen to be Chinese cloud storage (115, Quark, GuangYaPan). That detail does not matter for the rest of this post. The part that took real work was different: handing an LLM tools that move and delete files, and stopping it from doing something dumb with them. Three bugs taught me most of what I now believe about that. The shape of the thing The web app does almost nothing interesting. It writes a row to a Postgres queue and returns. A long-running worker picks up the row and starts a sandboxed agent. The agent gets a small set of tools: search resources, transfer a candidate, list a directory, move files into a season folder, mark episodes as obtained. Every tool runs through a deterministic workflow that owns the actual side effect. The agent proposes. The workflow decides whether the proposal is allowed, performs it, and reads the world back. That split is the whole design. The model is the part I cannot fully predict, so it gets the smallest possible blast radius. The deterministic code around it holds every irreversible action and every check. When I violated that split, things broke. They broke in the order below. Bug 1: the agent searched sixteen times and

2026-07-07 原文 →
AI 资讯

Why I stopped using online image compressors and built a CLI instead

Four years of optimizing React and Next.js projects taught me one thing: unoptimized images are everywhere, and nobody wants to fix them. Every project has the same pattern. Heavy PNG and JPG files are sitting inside /public , there is no consistent image pipeline, and some of those files have no business being that large in a production codebase. This is especially common in small and mid-sized projects. There is no CDN transformation layer or dedicated asset pipeline. Images get added while the product is moving quickly, and the cleanup becomes a task for “later.” Later, of course, never comes. Then, at 1am, while refactoring an extremely vibe-coded Next.js project, I found myself doing the cleanup manually again. Find an image. Upload it to an online compressor. Hit the free limit. Open another tool. Convert a few more. Download everything. Replace the original files. Hunt through the codebase for every import and src path. Hope I did not miss one. And I finally thought: I am a developer. Why am I doing this by hand? So I built pixcrush . npx pixcrush . One command to convert the images, compress them, and update their matching code references automatically. “But doesn’t Next.js already optimize images?” Yes, and if your application uses next/image consistently, you should absolutely take advantage of it. The Next.js <Image> component can resize images for different devices, lazy-load them, and serve modern formats such as WebP. Files inside /public can be referenced from the root URL, while statically imported images also give Next.js access to their intrinsic dimensions. The official Next.js image documentation explains these runtime optimizations in detail. But that solves a different layer of the problem. I wanted to clean up the source assets themselves: Replace heavy PNG and JPG files with smaller WebP files when conversion is worthwhile. Update existing imports and string-based image paths across the repository. Identify images that are no longer reference

2026-07-07 原文 →
AI 资讯

We built 126 browser tools with zero uploads. Here is what broke along the way

We are two friends building Pageonaut , a collection of 126 free browser tools (converters, calculators, PDF and image utilities, dev helpers). Early on we committed to one constraint: everything runs client-side . No file uploads, no accounts, no server-side processing. That one decision shaped the whole architecture, and it broke things in ways I did not expect. Here are the lessons, including the one where our server filled up with 419 GB of cache and took the site down twice. Why client-side only Every time I needed a quick converter, the top search results wanted me to upload my file to their server, create an account, or pay to remove a watermark. For work that a browser can trivially do locally. So the rule became: drop a file into one of our converters and it never leaves your device. You can watch the network tab while using it. This is great for privacy and trust, and it has a nice side effect: our server does almost nothing per user, so hosting stays cheap even if a tool gets popular. The cost: some tools are genuinely harder to build. PDF manipulation in the browser (we use client-side libraries instead of a server queue), image processing on the main thread without freezing the UI, and no "just call an API" escape hatch. When a tool truly needs the network (say, fetching a URL you give it), the page says so explicitly. Lesson 1: Unbounded URL params + ISR = a full disk This is the expensive one. We built shareable challenge pages: beat my score, try this color, that kind of thing. The URLs look like /tools/<slug>/challenge/<value> , where <value> is user-generated. With Next.js ISR, every unique URL that renders gets persisted to the filesystem cache. You can see where this is going. The value space is infinite. Bots found the pattern and started enumerating it. .next/server/app grew to 419 GB . The disk filled up, the site went down, and because we did not understand the root cause immediately, it happened a second time a few days later. The fix was tw

2026-07-07 原文 →
AI 资讯

Behind the Curtain: APE-QIL QUANTUM SUPREME OCTOPUS and the 3-Tier Sovereign Auth Pipeline

Most API authentication I've seen in production follows the same pattern: a single apiKey check at the top of each route handler, maybe a rate limiter slapped on as middleware, and a quota check that lives in the database layer. It works — until you have 673 routes across 3 access tiers, and you realize you can't answer the question "which routes require a paid subscription?" without grepping every file. I ran into this exact problem building the APE-QIL QUANTUM SUPREME OCTOPUS — a Bio-inspired Autonomous Intelligence Organism that operates as an AI routing platform with 14+ provider integrations. The codebase has 673 API routes divided into three access tiers: public (79 routes), free API key (337 routes), and paid subscription (255 routes). Manually maintaining auth on each route was untenable. So I built a composable request pipeline that makes the auth structure declarative and CI-enforced. This post walks through the architecture: the 3-tier sovereign auth model, the composable withRequestPipeline function, and the CI guard that fails the build if any protected route is missing its wrapper. The 3-Tier Sovereign Auth Model The access model is deliberately simple — three tiers, each with a clear boundary: // TIER 0 — Public, no auth (79 routes) // Health checks, pricing, blog, lead magnet, metrics // Example: /api/health, /api/pricing, /api/blog/* // TIER 1 — Free API key required (337 routes) // Any valid API key in the database grants access // Enforced via: withSovereignAuth('free') // Example: /api/v1/chat/completions (free tier limits) // TIER 2 — Paid subscription required (255 routes) // Requires Pro ($79/mo) or Business ($249/mo) tier // Enforced via: withSovereignAuth('professional') // Example: /api/v1/chat/completions (premium models, higher limits) The key design decision: the tier is declared at the route level, not inferred from the user's subscription at runtime. This means the route registry itself is the source of truth for "what requires what."

2026-07-06 原文 →
AI 资讯

10 Website Performance Optimization Tips Every Developer Should Know

Website performance is no longer just a nice-to-have feature—it's a critical factor for user experience, SEO, and business success. Even a one-second delay in page load time can reduce conversions and increase bounce rates. Whether you're building a portfolio, SaaS application, eCommerce platform, or business website, these optimization techniques can make a significant difference. Optimize Images Images are often the largest assets on a webpage. Use modern formats like AVIF or WebP, compress images, and serve responsive image sizes to reduce bandwidth usage. Self-Host Fonts Third-party font requests add latency. Self-hosting fonts, preloading critical font files, and serving only the required character subsets can dramatically improve loading performance. Remove Unused CSS & JavaScript Shipping unnecessary code increases download size and execution time. Tree shaking, code splitting, and removing unused styles help keep your bundle lean. Enable Caching Configure long-term browser caching for static assets and use hashed filenames for cache busting. This allows returning visitors to load your website much faster. Use Lazy Loading Images, videos, and iframes that aren't immediately visible should load only when needed. Native lazy loading is supported by modern browsers and is easy to implement. Optimize Core Web Vitals Google's Core Web Vitals measure how users experience your website. Focus on: Largest Contentful Paint (LCP) Interaction to Next Paint (INP) Cumulative Layout Shift (CLS) Improving these metrics benefits both SEO and user satisfaction. Minify Assets Minify HTML, CSS, and JavaScript files before deployment. Smaller files transfer faster and improve overall performance. Use a CDN Serving assets from edge locations around the world reduces latency and improves loading times for global visitors. Prioritize Accessibility Accessible websites provide a better experience for everyone and often align with SEO best practices. Use semantic HTML, descriptive labe

2026-07-06 原文 →
AI 资讯

Building in public, week 17: turning one feature into a page cluster (and the internal-linking layer nobody sees)

Week 16 shipped the AI background remover: Rust-native, ort + ISNet + libvips, no Python. That was the feature. Week 17 was not about writing more of it. It was about the boring, high-leverage part that most side projects skip: turning one working feature into pages that can actually rank, and wiring those pages together so search engines can find them. No new engine code this week. Just leverage on what already existed. Here is what that actually looked like. The problem: a hub with nothing pointing at it The background remover lives at /remove-background . That is the hub. The plan was classic hub-and-spoke: one general tool page, then use-case spokes that each target a specific intent (removing a signature background, prepping an Amazon product photo, and so on). I built two spokes this week. But halfway through, I looked at how internal links actually worked on the site and found the real problem: nothing linked from the hub to the spokes. The spokes linked back to the hub in their body text, but the hub had no idea they existed. Neither did the ~180 converter pages. Tool links on the site were hardcoded in a frontend constant, roughly: export const IMAGE_TOOLS = [ { label : " Compress JPG " , href : " /compress/jpg " , tool : " compress " }, { label : " Resize Image " , href : " /resize-image " , tool : " resize " }, { label : " Crop Image " , href : " /crop-image " , tool : " crop " }, { label : " Images to PDF " , href : " /images-to-pdf " , tool : " convert " }, ] as const ; That list covered the converter tools. It did not include the background remover or its spokes at all. So the new pages were orphans: reachable only through the sitemap, with no internal links carrying any signal to them. For a domain that is still young and still earning Google's trust, orphan pages get discovered slowly and rank even slower. The fix: one constant as the source of truth Instead of hardcoding links in three different places, I made a single constant describe the whole cl

2026-07-06 原文 →
AI 资讯

Opening .pages .numbers .keynote Files on Windows? I Built a Free iWork Viewer

If you've ever received a .pages or .numbers file on a Windows PC, you know the pain — you can't open it. No preview, no converter built in, and Apple's iCloud web tools are slow and clunky. So I built iworkviewer.com — a free, browser-based iWork file viewer and converter. No signup, no upload to any server. Everything happens in your browser. What it does Open .pages files → view them instantly, export to PDF or .docx Open .numbers files → view spreadsheets, export to .xlsx or PDF Open .keynote files → view presentations, export to PDF or .pptx Batch convert multiple iWork files at once The tech Built with Next.js, Cloudflare Pages, and pure client-side JavaScript. All file processing happens in the browser — your files never leave your computer. Zero server costs, zero privacy concerns. Why I built it I kept seeing Reddit threads and Quora questions: "How do I open a Pages file on Windows?" The answers were always the same — use iCloud.com (slow), download some sketchy converter (risky), or ask the sender to export as PDF first (annoying). I figured: if the browser can read a file, it can convert it. And it turns out, it can. Try it 👉 iworkviewer.com Open a .pages, .numbers, or .keynote file right in your browser. Free, forever, no account needed.

2026-07-01 原文 →
AI 资讯

I Moved My Next.js Dashboard Logic Into Postgres. My Frontend Got Boring (And That's the Point).

My dashboard had a useMemo doing arithmetic it had no business doing. It was a Pokémon TCG Pocket collection tracker, but that part doesn't matter. What matters is that the home page needed to show three things: overall completion, completion per set, and which set you were closest to finishing. The way I'd built it, the browser was fetching every card and every owned record, then grinding through the math on each render to figure all of that out. It worked. It also got slower and harder to read every time the data grew or I added a metric. So I moved the aggregation out of React and into Postgres, and the surprising result was that my frontend got boring . Fewer hooks, less state, almost nothing left to break. That's the whole argument of this post: aggregation belongs in the database, and when you put it there, the React code that's left over is the kind of boring you actually want in the layer your users touch. What "fetching everything into React" actually looks like Here's the shape of the original dashboard. Load all the cards, load the user's owned rows, then derive everything on the client. const [ cards , setCards ] = useState < Card [] > ([]); const [ owned , setOwned ] = useState < OwnedCard [] > ([]); useEffect (() => { ( async () => { const { data : allCards } = await supabase . from ( " cards " ). select ( " * " ); const { data : ownedRows } = await supabase . from ( " user_cards " ) . select ( " card_id " ); setCards ( allCards ?? []); setOwned ( ownedRows ?? []); })(); }, []); const ownedIds = useMemo (() => new Set ( owned . map (( o ) => o . card_id )), [ owned ]); const perSet = useMemo (() => { const groups : Record < string , { total : number ; have : number } > = {}; for ( const card of cards ) { const g = ( groups [ card . set_id ] ??= { total : 0 , have : 0 }); g . total += 1 ; if ( ownedIds . has ( card . id )) g . have += 1 ; } return groups ; }, [ cards , ownedIds ]); const overall = useMemo (() => { const total = cards . length ; const ha

2026-06-30 原文 →
AI 资讯

what i learned intentionally breaking hydration in next.js

i did something dumb last month. on purpose. i sat down, opened a next.js app, and tried to make hydration fail in every way i could think of. not because a bug forced me to. not because i was debugging something. just because i wanted to see it. understand it from the inside. and honestly? best few hours i've spent learning anything in a while. why i even did this you know how you use something for months and you think you get it, but you don't really get it? hydration was that for me. i knew the surface-level thing: server renders HTML, client takes over, they gotta match. cool. got it. moving on. except i didn't get it. i just got the vibe of it. every time i saw hydration mismatch, i'd ask claude, fix the immediate thing, feel vaguely annoyed, and move on. i never stopped to ask why that specific thing broke it. i was treating symptoms, not understanding the actual disease. so i decided to break it deliberately. if i caused the errors myself, i'd actually have to understand what i was doing. the setup basic next.js app. app router. a few pages. nothing fancy. i wasn't trying to build anything. i was trying to destroy something, carefully, so i could see what fell apart and why. break #1: the obvious one - new Date() on render this is the classic. everyone's seen it. export default function Page () { return < div > { new Date (). toLocaleString () } </ div > } server renders this at, say, 14:00:00. by the time react runs on the client and tries to reconcile, it's 14:00:01. the strings don't match. react screams. thing is, i knew this would happen. what i didn't think about was why react cares. here's the thing: react isn't doing a full diff on the entire DOM after hydration. it's trusting that the server HTML is a valid starting point and it's just attaching event listeners and state to it. but if the content doesn't match, it doesn't know what to trust. it can't partially hydrate "mostly correct" HTML. it either matches or it doesn't. so it throws the warning, a

2026-06-30 原文 →
AI 资讯

Building Innward: A B2B Hospitality Operating System with Vercel and Amazon Aurora

This blog post is created for the purposes of entering the Hack the Zero Stack with Vercel v0 and AWS Databases hackathon. #H0Hackathon Building Innward: A B2B Hospitality Operating System with Vercel and Amazon Aurora The hospitality industry is notorious for relying on "legacy" software—clunky, slow, and disconnected. For the Hack the Zero Stack hackathon, I set out to build Innward , a modern, AI-ready Property Management System (PMS) that proves you can build enterprise-grade B2B tools in record time using Vercel v0 and AWS Databases. The Vision: Moving Beyond the Spreadsheet Hotel managers don't just need a place to store "Room 101: Occupied." They need to solve the "Hidden Math" of revenue management. This means: Relational Complexity: Linking dates, room groups, and individual stays. Dynamic Pricing: Deriving rates based on occupancy and logic-based rules. Market Intelligence: Real-time benchmarking against competitors. The "Zero Stack": Vercel + Amazon Aurora To handle this complexity, I chose Amazon Aurora PostgreSQL (Serverless v2) . Why Aurora for B2B? In a B2B SaaS environment, data isolation and relational integrity are non-negotiable. Aurora provided the robust relational power needed to join complex pricing tables while scaling automatically as more hotels (tenants) join the platform. The Zero-Secret Architecture One of the most rewarding parts of this build was implementing the AWS RDS Signer . Following the "Zero Stack" philosophy, I moved away from static database passwords. Innward uses IAM-based authentication to communicate between Vercel and AWS. By utilizing the @aws-sdk/rds-signer , the application generates short-lived tokens on the fly. This means even if an environment variable were leaked, the database remains locked tight. // lib/db.ts snippet const signer = new Signer ({ credentials : awsCredentialsProvider ({ roleArn : process . env . AWS_ROLE_ARN ! , clientConfig : { region : process . env . AWS_REGION }, }), region : process . env .

2026-06-30 原文 →