feat: Next.js 14+ App Router Migration + Image Optimization#34
feat: Next.js 14+ App Router Migration + Image Optimization#34devin-ai-integration[bot] wants to merge 2 commits into
Conversation
- Upgrade next@14, react@18, react-dom@18, eslint-config-next@14
- Remove .babelrc and babel devDependencies (Next.js 14 uses SWC)
- Create App Router directory structure (app/)
- Migrate _app.js to app/layout.js with metadata API
- Extract client-side providers (StateContext, Toaster) into app/providers.js
- Migrate pages/index.js to app/page.js as async Server Component with ISR
- Migrate pages/product/[slug].js to app/product/[slug]/page.js with generateStaticParams
- Extract interactive product detail UI into ProductDetailClient.js ('use client')
- Migrate pages/success.js to app/success/page.js with 'use client' directive
- Migrate pages/api/stripe.js to app/api/stripe/route.js as Route Handler
- Replace all <img> tags with next/image <Image> component
- Configure next.config.js remotePatterns for cdn.sanity.io
- Add loading.js skeletons for home and product detail pages
- Add 'use client' directives to Navbar and Cart components
- Remove Head import from Layout.jsx (replaced by metadata API)
- Remove console.log from product detail page
- Delete old pages/ directory
Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…efix - Use parameterized GROQ query to prevent injection via slug parameter - Remove NEXT_PUBLIC_ prefix from Stripe secret key env var (server-only) Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
| name: item.name, | ||
| images: [newImage], | ||
| }, | ||
| unit_amount: item.price * 100, |
There was a problem hiding this comment.
🟡 Floating-point unit_amount may cause Stripe API rejection for decimal prices
At app/api/stripe/route.js:29, unit_amount: item.price * 100 can produce a non-integer due to floating-point arithmetic (e.g., 29.99 * 100 = 2998.9999999999995). The Stripe API requires unit_amount to be a positive integer in cents and will reject the request otherwise. This should use Math.round(item.price * 100) to ensure an integer value.
| unit_amount: item.price * 100, | |
| unit_amount: Math.round(item.price * 100), |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Good catch on the floating-point risk. However, this is pre-existing behavior — the original pages/api/stripe.js has the identical unit_amount: item.price * 100 on line 28. Since this PR's scope is migrating to App Router without changing business logic, I'll leave this as-is. A follow-up fix with Math.round() would be a good improvement though.
Summary
Upgrades the e-commerce app from Next.js 12 (Pages Router) to Next.js 14 (App Router) and replaces all
<img>tags withnext/image<Image>components.Framework upgrades:
next12.1.0 → 14,react17.0.2 → 18,react-dom17.0.2 → 18,eslint-config-next12 → 14.babelrcand babel devDependencies (Next.js 14 uses SWC)Pages Router → App Router migration:
pages/_app.js→app/layout.js(metadata API) +app/providers.js('use client'wrapper for StateContext/Toaster)pages/index.js→app/page.js— async Server Component withrevalidate = 60(ISR, previously SSR viagetServerSideProps)pages/product/[slug].js→app/product/[slug]/page.js+ProductDetailClient.js— Server Component for data fetching withgenerateStaticParams, interactive UI extracted to a'use client'componentpages/success.js→app/success/page.jswith'use client'pages/api/stripe.js→app/api/stripe/route.jsusingNextResponseloading.jsskeletons for home and product detail routes'use client'toNavbar.jsxandCart.jsxconsole.log(product)from product detail pagepages/directoryImage optimization:
<img>tags replaced with<Image>fromnext/imageusing explicit.url()on SanityurlFor()next.config.jsconfigured withremotePatternsforcdn.sanity.ioSecurity fixes (from review):
NEXT_PUBLIC_STRIPE_SECRET_KEY→STRIPE_SECRET_KEYto prevent client-side exposure$slugparam) to prevent injectionBuild (
npm run build) and lint (npm run lint) pass successfully.Review & Testing Checklist for Human
NEXT_PUBLIC_STRIPE_SECRET_KEYtoSTRIPE_SECRET_KEY. Update your.env/ hosting environment accordingly, or the Stripe checkout will fail.urlFor(...).url()call needs a valid Sanity image source. Ifimageis undefined/falsy (e.g.,urlFor(image && image[0]).url()whereimageis empty),.url()will throw instead of silently producing a badsrc. Run the app and check all image-heavy views (home, product detail, cart).getServerSideProps(fresh on every request) torevalidate = 60(stale for up to 60s). Verify this is acceptable for content freshness requirements.request.headers.get('origin')instead ofreq.headers.origin, and error responses changed shape (now{ message: ... }instead of raw string). Verify the full checkout → success redirect works.Layout.jsxis now dead code — The root layout (app/layout.js) directly renders Navbar/Footer.components/Layout.jsxstill exists and is exported fromcomponents/index.jsbut is no longer imported anywhere. Consider removing it if it's truly unused.Recommended test plan: Run
npm run dev, navigate the home page, click a product, add to cart, and attempt checkout. Verify all images load vianext/image(check network tab for/_next/imageURLs).Notes
pages/api/hello.js(default Next.js scaffold route) was also removed as part of deletingpages/.--legacy-peer-depswas needed during install becausenext-sanity-image@3.2.1has a peer dependency onnext@^11 || ^12.useEffectmissing dependencies warning inapp/success/page.jsis pre-existing from the original code and was intentionally left unchanged per instructions not to change state management approach.Link to Devin session: https://app.devin.ai/sessions/76b6b86c8b604965b9a3035731f44946
Requested by: @Colhodm