<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: KahWee Teng</title>
    <description>The latest articles on Forem by KahWee Teng (@kahwee).</description>
    <link>https://forem.com/kahwee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3161395%2F2a8ba4ac-9318-4a86-b6c0-bece43e29d81.jpg</url>
      <title>Forem: KahWee Teng</title>
      <link>https://forem.com/kahwee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kahwee"/>
    <language>en</language>
    <item>
      <title>Sora Isn't the Problem: It's the Mirror</title>
      <dc:creator>KahWee Teng</dc:creator>
      <pubDate>Mon, 20 Oct 2025 04:50:32 +0000</pubDate>
      <link>https://forem.com/kahwee/sora-isnt-the-problem-its-the-mirror-hd8</link>
      <guid>https://forem.com/kahwee/sora-isnt-the-problem-its-the-mirror-hd8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was originally published on &lt;a href="https://kahwee.com/2025/sora-isnt-the-problem/" rel="noopener noreferrer"&gt;KahWee's blog&lt;/a&gt;. Read more on the original site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Real Problem
&lt;/h2&gt;

&lt;p&gt;I finally got access to Sora in TikTok form, and something clicked. This isn't annoying because Sora is bad. It's annoying because it's honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Problem: Friction Is Gone
&lt;/h2&gt;

&lt;p&gt;Here's the insight: lying used to have a cost. If you wanted to fabricate video evidence, you had to &lt;em&gt;work&lt;/em&gt;. Film it. Edit it. Make it convincing. That work was friction. Friction meant there was a penalty for lying—time, effort, risk of being caught in the production.&lt;/p&gt;

&lt;p&gt;Sora removes that penalty entirely.&lt;/p&gt;

&lt;p&gt;Now you prompt an AI. You want a fake historical event? Seconds. Celebrity deepfake? Done. False testimony on video? Trivial. The cost of lying just collapsed to zero. It's now &lt;em&gt;easier&lt;/em&gt; to fabricate than to capture reality.&lt;/p&gt;

&lt;p&gt;This changes everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Breaks the System
&lt;/h2&gt;

&lt;p&gt;Social media has always optimized for engagement over truth. But engagement-over-truth only works if truth is expensive enough to be selective. You can't put &lt;em&gt;everything&lt;/em&gt; on the feed. You have to choose.&lt;/p&gt;

&lt;p&gt;With friction, lying is &lt;em&gt;selective&lt;/em&gt;. You lie when it matters, when the payoff justifies the work. The system survives because most content is still just... regular stuff. People sharing their lives. Creators doing legitimate work.&lt;/p&gt;

&lt;p&gt;Sora removes that selection. Now lying is &lt;em&gt;free&lt;/em&gt;. So the system optimizes: if truth and lies cost the same to produce, and if engagement cares about neither, then the algorithm picks based on one thing: what keeps you scrolling?&lt;/p&gt;

&lt;p&gt;The answer isn't truth. It's novelty. Spectacle. Uncanniness. Deepfakes of celebrities doing weird things are more engaging than someone filming their actual day.&lt;/p&gt;

&lt;p&gt;Once friction is gone, the system &lt;em&gt;must&lt;/em&gt; fill the feed with fabrication. Not because creators are evil. But because the incentives make fabrication &lt;em&gt;cheaper than reality&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like
&lt;/h2&gt;

&lt;p&gt;Echo chambers accelerate. TikTok already concentrates algorithmic curation into bubbles. Sora makes that bubble self-reinforcing: same IP, same memes, same likenesses, generated infinitely. Homogeneity at scale. The machine feeding itself.&lt;/p&gt;

&lt;p&gt;Truth becomes optional. Audiences stop sorting by "real vs. fake" and start sorting by "entertaining vs. dull." The Verge reported getting trapped in scroll loops of deepfaked celebrities and fabricated moments—even knowing they were AI. The uncanniness is &lt;em&gt;the feature&lt;/em&gt;. Plausibility doesn't require proof anymore.&lt;/p&gt;

&lt;p&gt;Persuasion decouples from evidence. Short-form video already rewards punchy narratives over grounded reporting. Sora makes that worse: you can &lt;em&gt;visually assert&lt;/em&gt; anything now. Any scene. Any dialogue. Any context. Spectacle beats verification. Virality beats truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Epiphany
&lt;/h2&gt;

&lt;p&gt;Here's what hit me: Sora isn't &lt;em&gt;breaking&lt;/em&gt; social media. It's just making visible what was always there.&lt;/p&gt;

&lt;p&gt;We built platforms optimized for engagement. Then we added algorithms designed to concentrate attention. Then we added infinite scroll and habit-forming UI. Then we added unlimited content generation via AI.&lt;/p&gt;

&lt;p&gt;At each step, we told ourselves it was fine. "Engagement metrics are just incentives." "Echo chambers are just efficiency." "Disinformation is a problem we'll solve later."&lt;/p&gt;

&lt;p&gt;But watching Sora—watching people trapped in loops of fabricated celebrity cameos—you can't pretend anymore. The problem isn't that AI is &lt;em&gt;too good&lt;/em&gt; at generating video. The problem is that friction is &lt;em&gt;gone&lt;/em&gt;. When lying costs nothing and engagement doesn't care about truth, the system must fill itself with slop.&lt;/p&gt;

&lt;p&gt;That's not a bug. That's the machine working exactly as designed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Friction Did For Us
&lt;/h2&gt;

&lt;p&gt;We didn't realize it, but friction was a natural check on this. Video production was slow. Editing took time. Deepfakes required skill. There was a &lt;em&gt;price&lt;/em&gt; for lying on camera. Most people paid it in truth instead.&lt;/p&gt;

&lt;p&gt;Now there is no price. Lying is as effortless as telling the truth. The system was never equipped to handle that. It was designed assuming &lt;em&gt;some&lt;/em&gt; friction. Some cost to fabrication. Some incentive to be selective about deception.&lt;/p&gt;

&lt;p&gt;Sora removes that assumption.&lt;/p&gt;

&lt;p&gt;The result is what you're seeing: infinite scroll of fabricated content, algorithmically sorted for engagement, presented in a UI designed for habit formation, watched by people who stopped caring if it's real.&lt;/p&gt;

&lt;p&gt;That's not a problem with Sora. That's what happens when you remove friction from a system designed to exploit engagement above all else.&lt;/p&gt;

&lt;p&gt;The machine didn't break. It just stopped pretending.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related posts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://future.forem.com/2025/ai-the-new-backdoor-layoff/"&gt;AI The New Backdoor Layoff&lt;/a&gt; - How companies use AI as cover for not hiring&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://future.forem.com/2025/on-technological-stratification/"&gt;On Technological Stratification&lt;/a&gt; - How AI widens inequality and access gaps&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://future.forem.com/2025/perplexity-replaces-google-search-and-apple-news-for-me/"&gt;Perplexity replaces Google Search and Apple News for me&lt;/a&gt; - How AI is changing information consumption&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://future.forem.com/2025/google-ai-overviews-cutting-web-traffic-in-half/"&gt;Google AI Overviews are cutting web traffic in half&lt;/a&gt; - The impact of AI on traditional web patterns&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>socialmedia</category>
      <category>technology</category>
      <category>analysis</category>
    </item>
    <item>
      <title>Migrating from Remix to React Router v7</title>
      <dc:creator>KahWee Teng</dc:creator>
      <pubDate>Mon, 20 Oct 2025 04:50:17 +0000</pubDate>
      <link>https://forem.com/kahwee/migrating-from-remix-to-react-router-v7-4gfo</link>
      <guid>https://forem.com/kahwee/migrating-from-remix-to-react-router-v7-4gfo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was originally published on &lt;a href="https://kahwee.com/2025/migrating-from-remix-to-react-router-v7/" rel="noopener noreferrer"&gt;KahWee's blog&lt;/a&gt;. Read more on the original site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why I Switched
&lt;/h2&gt;

&lt;p&gt;Last weekend, I made the decision to migrate one of my full-stack React applications from Remix to React Router v7 framework mode. The migration took about two days and went surprisingly smooth - here's why I made the switch, what the process entailed, and the practical insights that made it successful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why React Router v7?
&lt;/h2&gt;

&lt;p&gt;React Router v7 is Remix v3 renamed. Ryan Florence and Michael Jackson merged the projects because "Remix v2 had become such a thin wrapper around React Router that an artificial separation developed between the two projects."&lt;/p&gt;

&lt;p&gt;The practical benefits are immediate: instead of juggling &lt;code&gt;@remix-run/node&lt;/code&gt;, &lt;code&gt;@remix-run/react&lt;/code&gt;, &lt;code&gt;@remix-run/serve&lt;/code&gt;, and others, everything consolidates into the unified &lt;code&gt;react-router&lt;/code&gt; package. My dependencies dropped from 16 to 3. The new &lt;code&gt;react-router typegen&lt;/code&gt; command eliminates manual type annotations in loader functions. The Vite-based build system is cleaner than Remix's custom setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Toughest Part: TypeScript Type System Overhaul
&lt;/h2&gt;

&lt;p&gt;While the API compatibility made most changes straightforward, &lt;strong&gt;TypeScript integration was by far the most challenging aspect&lt;/strong&gt; of the migration. The git log reveals multiple commits dedicated to resolving type issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LoaderFunctionArgs and useLoaderData typing&lt;/strong&gt;: Every route needed manual type updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route module type safety&lt;/strong&gt;: New type patterns required learning React Router v7's approach&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface mismatches&lt;/strong&gt;: Database query results needed type alignment with component expectations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case-sensitivity bugs&lt;/strong&gt;: Database queries failed due to case-sensitive matching that worked in Remix&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiple commits were dedicated to resolving TypeScript errors, indicating hours of type debugging across the migration.&lt;/p&gt;

&lt;p&gt;This taught me that &lt;strong&gt;React Router v7's type system is more strict&lt;/strong&gt; than Remix v2, which is ultimately beneficial but creates friction during migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Overview: What Actually Changed
&lt;/h2&gt;

&lt;p&gt;The migration touched &lt;strong&gt;40 files&lt;/strong&gt; with &lt;strong&gt;327 insertions and 918 deletions&lt;/strong&gt; - a net reduction of 591 lines. Here's the breakdown:&lt;/p&gt;

&lt;h3&gt;
  
  
  Package Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- "@remix-run/node": "^2.17.0"
- "@remix-run/react": "^2.17.0"
- "@remix-run/serve": "^2.17.0"
- "@remix-run/dev": "^2.17.0"
&lt;/span&gt;&lt;span class="gi"&gt;+ "@react-router/node": "^7.8.2"
+ "@react-router/serve": "^7.8.2"
+ "@react-router/dev": "^7.8.2"
+ "react-router": "^7.8.2"
+ "react-router-dom": "^7.8.2"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build Scripts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- "build": "remix vite:build"
- "dev": "remix vite:dev"
- "start": "remix-serve ./build/server/index.js"
&lt;/span&gt;&lt;span class="gi"&gt;+ "build": "react-router build"
+ "dev": "react-router dev"
+ "start": "react-router-serve ./build/server/index.js"
+ "typecheck": "react-router typegen &amp;amp;&amp;amp; tsc"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vite Configuration
&lt;/h3&gt;

&lt;p&gt;The Vite config transformation was dramatic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import { vitePlugin as remix } from '@remix-run/dev';
- // Complex remix configuration with future flags
- remix({
-   future: {
-     v3_fetcherPersist: true,
-     v3_relativeSplatPath: true,
-     v3_throwAbortReason: true,
-     v3_singleFetch: true,
-     v3_lazyRouteDiscovery: true,
-   }
- })
&lt;/span&gt;&lt;span class="gi"&gt;+ import { reactRouter } from '@react-router/dev/vite';
+ // Simple, clean configuration
+ plugins: [reactRouter(), tsconfigPaths(), tailwindcss()]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Import Updates Across Components
&lt;/h3&gt;

&lt;p&gt;Every route and component needed import updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import type { LoaderFunctionArgs } from '@remix-run/node';
- import { useLoaderData } from '@remix-run/react';
&lt;/span&gt;&lt;span class="gi"&gt;+ import type { LoaderFunctionArgs } from 'react-router';
+ import { useLoaderData } from 'react-router';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Step-by-Step Migration Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Phase 1: Dependencies and Configuration (30 minutes)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Package.json overhaul&lt;/strong&gt;: Replaced all &lt;code&gt;@remix-run/*&lt;/code&gt; packages with React Router equivalents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Script updates&lt;/strong&gt;: Changed build/dev commands to use &lt;code&gt;react-router&lt;/code&gt; CLI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite configuration&lt;/strong&gt;: Simplified from complex Remix config to minimal React Router setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New config file&lt;/strong&gt;: Added &lt;code&gt;react-router.config.ts&lt;/code&gt; for framework-specific settings&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Phase 2: Import Refactoring (90 minutes)
&lt;/h3&gt;

&lt;p&gt;This was the most time-consuming phase. Every file importing from &lt;code&gt;@remix-run/*&lt;/code&gt; needed updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Route files&lt;/strong&gt; (23 files): Updated loader/action imports and useLoaderData calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component files&lt;/strong&gt; (8 files): Changed Link and Form imports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entry files&lt;/strong&gt; (2 files): Updated server/client entry points&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Utility files&lt;/strong&gt; (7 files): Modified auth and theme utilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process was methodical: start with entry points, then routes, then components, fixing TypeScript errors as they appeared.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Configuration Fine-tuning (30 minutes)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript config&lt;/strong&gt;: Updated to use React Router types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route file naming&lt;/strong&gt;: Converted splat routes from &lt;code&gt;api.auth.$.ts&lt;/code&gt; to &lt;code&gt;api.auth.$rest.tsx&lt;/code&gt; for clarity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development scripts&lt;/strong&gt;: Added the new &lt;code&gt;react-router typegen&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding React Router Typegen: The Game Changer
&lt;/h2&gt;

&lt;p&gt;One feature that took me time to fully appreciate was &lt;code&gt;react-router typegen&lt;/code&gt;. &lt;strong&gt;This wasn't available in Remix&lt;/strong&gt; - it's a React Router v7 innovation that represents a significant leap in TypeScript integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  What React Router Typegen Does
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;typegen&lt;/code&gt; command generates route-specific TypeScript types automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;react-router typegen
&lt;span class="c"&gt;# Generates types in .react-router/types/ directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It creates a &lt;code&gt;+types/&amp;lt;route-file&amp;gt;.d.ts&lt;/code&gt; for each route, providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic type inference&lt;/strong&gt; for loader data without manual interfaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route-specific parameter typing&lt;/strong&gt; (URL params, search params)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action and loader return type validation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component prop type safety&lt;/strong&gt; based on your actual route implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Rationale Behind Typegen
&lt;/h3&gt;

&lt;p&gt;React Router's type generation executes your route config (&lt;code&gt;app/routes.ts&lt;/code&gt;) to determine routes, then generates corresponding TypeScript definitions. This &lt;strong&gt;build-time analysis&lt;/strong&gt; provides runtime safety without polluting the API design.&lt;/p&gt;

&lt;p&gt;Instead of manually defining &lt;code&gt;LoaderData&lt;/code&gt; interfaces for every route, typegen infers them from your actual loader implementations. This eliminates the common Remix pattern of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Old Remix pattern - manual interface definition&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;LoaderData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Recipe&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LoaderData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With React Router v7, you simply write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// New React Router v7 pattern - types inferred automatically&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;LoaderFunctionArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Return whatever you want, typegen handles the interface&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getRecipes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Component gets proper typing automatically&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RecipeList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// recipes and user are fully typed without manual interfaces&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type inference approach fixes the interface drift problem that plagued my Remix codebase. With manual interfaces, you change a loader to return additional data but forget to update the interface. TypeScript doesn't complain because the interface still "works" - it just silently ignores the new properties.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useLoaderData&amp;lt;typeof loader&amp;gt;&lt;/code&gt; eliminates this entirely. The type reflects your actual loader implementation, not what you think it should return. When you refactor a loader's return structure, every component using that data updates automatically.&lt;/p&gt;

&lt;p&gt;Here's where this really helps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;LoaderFunctionArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAuthUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requiresAuth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserRecipes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requiresAuth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;totalCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RecipePage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// TypeScript knows data.requiresAuth is boolean&lt;/span&gt;
  &lt;span class="c1"&gt;// data.totalCount only exists when user is present&lt;/span&gt;
  &lt;span class="c1"&gt;// data.user is exactly User | null based on the conditional logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The manual interface approach would require defining a union type or optional properties, then keeping that interface in sync with the loader logic. With type inference, your implementation is the contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happened to Remix?
&lt;/h2&gt;

&lt;p&gt;Ryan Florence and Michael Jackson handed React Router v7 to an open governance committee while they focus on Remix v3. But Remix v3 isn't an iteration - it's a complete rewrite with no React dependencies. They're building on a fork of Preact for "AI-first development" and "AI driven user interfaces." This leaves React Router v7 as the clear successor for production React applications that want the Remix experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Notes
&lt;/h2&gt;

&lt;p&gt;The API compatibility made most changes straightforward - loaders, actions, and components work identically. The migration was primarily changing imports and build configuration.&lt;/p&gt;

&lt;p&gt;Some gotchas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route files with &lt;code&gt;.ts&lt;/code&gt; extensions needed to become &lt;code&gt;.tsx&lt;/code&gt; for React Router v7 to recognize them&lt;/li&gt;
&lt;li&gt;The entry.server.tsx got much simpler - React Router v7 removed the bot/browser request splitting logic&lt;/li&gt;
&lt;li&gt;Bundle size dropped ~30% and build times improved with the unified package structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI assistance was critical for batch import updates across 40+ files. What could have been a full day of manual work became two focused sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth the Switch?
&lt;/h2&gt;

&lt;p&gt;React Router v7 is actively developed while Remix v2 is in maintenance mode. The simplified dependency tree, better TypeScript integration, and improved build system provide immediate benefits. For teams on Remix v2 with future flags enabled, the migration is straightforward.&lt;/p&gt;

&lt;p&gt;Whether coming from Remix or considering alternatives to Next.js, React Router v7 framework mode handles full-stack React applications well.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The migration is complete and running smoothly on React Router v7. The process has proven that React Router v7 framework mode is a worthy successor to Remix, offering the same power with simplified tooling and enhanced type safety.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>migration</category>
    </item>
    <item>
      <title>AI Overviews are cutting web traffic in half</title>
      <dc:creator>KahWee Teng</dc:creator>
      <pubDate>Mon, 20 Oct 2025 04:48:38 +0000</pubDate>
      <link>https://forem.com/kahwee/ai-overviews-are-cutting-web-traffic-in-half-1jml</link>
      <guid>https://forem.com/kahwee/ai-overviews-are-cutting-web-traffic-in-half-1jml</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was originally published on &lt;a href="https://kahwee.com/2025/google-ai-overviews-cutting-web-traffic-in-half/" rel="noopener noreferrer"&gt;KahWee's blog&lt;/a&gt;. Read more on the original site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;New research from the Pew Research Center, as &lt;a href="https://arstechnica.com/ai/2025/07/research-shows-google-ai-overviews-reduce-website-clicks-by-almost-half/" rel="noopener noreferrer"&gt;reported by Ars Technica&lt;/a&gt;, reveals that Google's AI Overviews are significantly impacting website traffic. According to the study, when AI-generated summaries appear at the top of Google search results, users are almost half as likely to click through to other websites—dropping from a 15% to an 8% click rate.&lt;/p&gt;

&lt;p&gt;Even more striking, just 1% of users click on the sources cited within the AI Overviews, with Wikipedia, YouTube, and Reddit being the most frequently referenced. The study found that about 1 in 5 Google searches now display these AI Overviews, especially for longer, question-based queries.&lt;/p&gt;

&lt;p&gt;Despite Google's claims that AI features drive engagement and new opportunities for websites, the data suggests users are more likely to end their search after reading an AI-generated summary—potentially leaving them with incomplete or even incorrect information, as generative AI is known to occasionally produce errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  My experience mirrors this trend
&lt;/h2&gt;

&lt;p&gt;I use Dia, a browser with an LLM model built in, and I love it. I barely use Google anymore. With information synthesized directly, I rarely visit websites either. Getting immediate, contextualized answers without clicking through multiple sites has completely changed how I consume information.&lt;/p&gt;

&lt;p&gt;This behavioral shift raises fascinating questions about the future of the web. Are we witnessing the emergence of the "dead internet theory" in practice? When AI systems can synthesize and present information without requiring users to visit original sources, what happens to the web's fundamental click-through economy?&lt;/p&gt;

</description>
      <category>seo</category>
      <category>google</category>
      <category>ai</category>
      <category>analysis</category>
    </item>
  </channel>
</rss>
