From c6b53eb538cc4e5add7b70980ebe85eb7fb50e10 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Mon, 1 Sep 2025 16:31:18 -0700 Subject: [PATCH] Migrate all remaining components to App Router navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 'use client' directives to all remaining components - Update imports from next/router to next/navigation - Replace router.locale with getCookie('NEXT_LOCALE') - Update LanguageSwitch to use router.refresh() - Replace router.asPath with usePathname() Components migrated: - Common: SelectWithInput, Editor - Layout: Header, Layout, MentionList, ElementToggle - Misc: UpdateToast, SearchModal, RaidCombobox, FilterModal - Misc: ChangelogUnit, HovercardHeader, LanguageSwitch - Extra: GuidebookUnit, GuidebookResult - Reps: GridRep, CharacterRep, WeaponRep, SummonRep Added migration PRD documentation to track progress. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- components/ElementToggle/index.tsx | 9 +- components/Header/index.tsx | 10 +- components/HovercardHeader/index.tsx | 7 +- components/LanguageSwitch/index.tsx | 7 +- components/Layout/index.tsx | 7 +- components/MentionList/index.tsx | 6 +- components/about/ChangelogUnit/index.tsx | 9 +- components/common/Editor/index.tsx | 8 +- components/common/SelectWithInput/index.tsx | 7 +- components/extra/GuidebookResult/index.tsx | 7 +- components/extra/GuidebookUnit/index.tsx | 7 +- components/filters/FilterModal/index.tsx | 7 +- components/raids/RaidCombobox/index.tsx | 8 +- components/reps/CharacterRep/index.tsx | 7 +- components/reps/GridRep/index.tsx | 7 +- components/reps/SummonRep/index.tsx | 7 +- components/reps/WeaponRep/index.tsx | 7 +- components/search/SearchModal/index.tsx | 7 +- components/toasts/UpdateToast/index.tsx | 2 +- prd/app-router-migration.md | 209 ++++++++++++++++++++ 20 files changed, 272 insertions(+), 73 deletions(-) create mode 100644 prd/app-router-migration.md diff --git a/components/ElementToggle/index.tsx b/components/ElementToggle/index.tsx index b87e85fb..75dd5474 100644 --- a/components/ElementToggle/index.tsx +++ b/components/ElementToggle/index.tsx @@ -1,5 +1,6 @@ +'use client' import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { useTranslation } from 'next-i18next' import classNames from 'classnames' @@ -12,10 +13,8 @@ interface Props { } const ElementToggle = ({ currentElement, sendValue, ...props }: Props) => { - // Router and localization - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + // Localization + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const { t } = useTranslation('common') diff --git a/components/Header/index.tsx b/components/Header/index.tsx index 4cba70d0..67d920c3 100644 --- a/components/Header/index.tsx +++ b/components/Header/index.tsx @@ -1,6 +1,6 @@ +'use client' import React, { useState } from 'react' -import { deleteCookie } from 'cookies-next' -import { useRouter } from 'next/router' +import { deleteCookie, getCookie } from 'cookies-next' import { useTranslation } from 'next-i18next' import classNames from 'classnames' import clonedeep from 'lodash.clonedeep' @@ -37,10 +37,8 @@ const Header = () => { // Localization const { t } = useTranslation('common') - // Router - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + // Locale + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // State management const [alertOpen, setAlertOpen] = useState(false) diff --git a/components/HovercardHeader/index.tsx b/components/HovercardHeader/index.tsx index 7af01f09..29691bd5 100644 --- a/components/HovercardHeader/index.tsx +++ b/components/HovercardHeader/index.tsx @@ -1,4 +1,5 @@ -import { useRouter } from 'next/router' +'use client' +import { getCookie } from 'cookies-next' import UncapIndicator from '~components/uncap/UncapIndicator' import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon' @@ -28,9 +29,7 @@ const Proficiency = [ ] const HovercardHeader = ({ gridObject, object, type, ...props }: Props) => { - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const overlay = () => { if (type === 'character') { diff --git a/components/LanguageSwitch/index.tsx b/components/LanguageSwitch/index.tsx index ba42bbce..21049fe8 100644 --- a/components/LanguageSwitch/index.tsx +++ b/components/LanguageSwitch/index.tsx @@ -1,5 +1,7 @@ +'use client' import React, { PropsWithChildren, useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { useRouter } from 'next/navigation' +import { usePathname } from 'next/navigation' import { setCookie } from 'cookies-next' import { retrieveLocaleCookies } from '~utils/retrieveCookies' import * as SwitchPrimitive from '@radix-ui/react-switch' @@ -14,6 +16,7 @@ export const LanguageSwitch = React.forwardRef( ) { // Router and locale data const router = useRouter() + const pathname = usePathname() const localeData = retrieveLocaleCookies() // State @@ -30,7 +33,7 @@ export const LanguageSwitch = React.forwardRef( expiresAt.setDate(expiresAt.getDate() + 120) setCookie('NEXT_LOCALE', language, { path: '/', expires: expiresAt }) - router.push(router.asPath, undefined, { locale: language }) + router.refresh() } return ( diff --git a/components/Layout/index.tsx b/components/Layout/index.tsx index 8664c112..4b811dcc 100644 --- a/components/Layout/index.tsx +++ b/components/Layout/index.tsx @@ -1,5 +1,6 @@ +'use client' import { PropsWithChildren, useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { usePathname } from 'next/navigation' import { add, format } from 'date-fns' import { getCookie } from 'cookies-next' @@ -11,7 +12,7 @@ import UpdateToast from '~components/toasts/UpdateToast' interface Props {} const Layout = ({ children }: PropsWithChildren) => { - const router = useRouter() + const pathname = usePathname() const [updateToastOpen, setUpdateToastOpen] = useState(false) useEffect(() => { @@ -48,7 +49,7 @@ const Layout = ({ children }: PropsWithChildren) => { } const updateToast = () => { - const path = router.asPath.replaceAll('/', '') + const path = pathname.replaceAll('/', '') return ( !['about', 'updates', 'roadmap'].includes(path) && diff --git a/components/MentionList/index.tsx b/components/MentionList/index.tsx index e90d6336..fb9207de 100644 --- a/components/MentionList/index.tsx +++ b/components/MentionList/index.tsx @@ -1,3 +1,4 @@ +'use client' import React, { forwardRef, useEffect, @@ -5,7 +6,7 @@ import React, { useState, } from 'react' import { useTranslation } from 'next-i18next' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { SuggestionProps } from '@tiptap/suggestion' import classNames from 'classnames' @@ -34,8 +35,7 @@ interface MentionProps extends SuggestionProps { export const MentionList = forwardRef( ({ items, ...props }: Props, forwardedRef) => { - const router = useRouter() - const locale = router.locale || 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const { t } = useTranslation('common') diff --git a/components/about/ChangelogUnit/index.tsx b/components/about/ChangelogUnit/index.tsx index 1ae9bc5c..6451db43 100644 --- a/components/about/ChangelogUnit/index.tsx +++ b/components/about/ChangelogUnit/index.tsx @@ -1,5 +1,6 @@ -import { useRouter } from 'next/router' +'use client' import React, { useEffect, useState } from 'react' +import { getCookie } from 'cookies-next' import api from '~utils/api' import styles from './index.module.scss' @@ -19,10 +20,8 @@ const defaultProps = { } const ChangelogUnit = ({ id, type, image }: Props) => { - // Router - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + // Locale + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // State const [item, setItem] = useState() diff --git a/components/common/Editor/index.tsx b/components/common/Editor/index.tsx index f99c1d56..fb6dda9f 100644 --- a/components/common/Editor/index.tsx +++ b/components/common/Editor/index.tsx @@ -1,6 +1,7 @@ +'use client' import { ComponentProps, useCallback, useEffect } from 'react' import { useEditor, EditorContent } from '@tiptap/react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { useTranslation } from 'next-i18next' import StarterKit from '@tiptap/starter-kit' @@ -44,9 +45,8 @@ const Editor = ({ onUpdate, ...props }: Props) => { - // Hooks: Router - const router = useRouter() - const locale = router.locale || 'en' + // Hooks: Locale + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const { t } = useTranslation('common') diff --git a/components/common/SelectWithInput/index.tsx b/components/common/SelectWithInput/index.tsx index d6caaa41..d7f41b13 100644 --- a/components/common/SelectWithInput/index.tsx +++ b/components/common/SelectWithInput/index.tsx @@ -1,6 +1,7 @@ +'use client' // Core dependencies import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { useTranslation } from 'next-i18next' import classNames from 'classnames' @@ -39,9 +40,7 @@ const SelectWithInput = ({ sendValidity, sendValues, }: Props) => { - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const { t } = useTranslation('common') // UI state diff --git a/components/extra/GuidebookResult/index.tsx b/components/extra/GuidebookResult/index.tsx index 44b2babc..ce986bb4 100644 --- a/components/extra/GuidebookResult/index.tsx +++ b/components/extra/GuidebookResult/index.tsx @@ -1,5 +1,6 @@ +'use client' import React from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import styles from './index.module.scss' @@ -9,9 +10,7 @@ interface Props { } const GuidebookResult = (props: Props) => { - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const guidebook = props.data diff --git a/components/extra/GuidebookUnit/index.tsx b/components/extra/GuidebookUnit/index.tsx index 133b6571..6504310a 100644 --- a/components/extra/GuidebookUnit/index.tsx +++ b/components/extra/GuidebookUnit/index.tsx @@ -1,5 +1,6 @@ +'use client' import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { Trans, useTranslation } from 'next-i18next' import classNames from 'classnames' @@ -36,9 +37,7 @@ const GuidebookUnit = ({ }: Props) => { // Translations and locale const { t } = useTranslation('common') - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // State: UI const [searchModalOpen, setSearchModalOpen] = useState(false) diff --git a/components/filters/FilterModal/index.tsx b/components/filters/FilterModal/index.tsx index 1bd5d2df..760e1f45 100644 --- a/components/filters/FilterModal/index.tsx +++ b/components/filters/FilterModal/index.tsx @@ -1,6 +1,6 @@ +'use client' import React, { useEffect, useRef, useState } from 'react' import { getCookie, setCookie } from 'cookies-next' -import { useRouter } from 'next/router' import { Trans, useTranslation } from 'react-i18next' import { Dialog, DialogTrigger } from '~components/common/Dialog' @@ -33,9 +33,8 @@ const MAX_WEAPONS = 13 const MAX_SUMMONS = 8 const FilterModal = (props: Props) => { - // Set up router - const router = useRouter() - const locale = router.locale + // Set up locale + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // Set up translation const { t } = useTranslation('common') diff --git a/components/raids/RaidCombobox/index.tsx b/components/raids/RaidCombobox/index.tsx index dad4a3b8..d2277fe8 100644 --- a/components/raids/RaidCombobox/index.tsx +++ b/components/raids/RaidCombobox/index.tsx @@ -1,5 +1,6 @@ +'use client' import { createRef, useCallback, useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { useTranslation } from 'react-i18next' import classNames from 'classnames' @@ -66,9 +67,8 @@ interface Props { } const RaidCombobox = (props: Props) => { - // Set up router for locale - const router = useRouter() - const locale = router.locale || 'en' + // Set up locale from cookie + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // Set up translations const { t } = useTranslation('common') diff --git a/components/reps/CharacterRep/index.tsx b/components/reps/CharacterRep/index.tsx index 1c72fac5..26a4404f 100644 --- a/components/reps/CharacterRep/index.tsx +++ b/components/reps/CharacterRep/index.tsx @@ -1,5 +1,6 @@ +'use client' import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import { useTranslation } from 'next-i18next' import 'fix-date' @@ -17,10 +18,8 @@ const CHARACTERS_COUNT = 3 const CharacterRep = (props: Props) => { // Localization for alt tags - const router = useRouter() const { t } = useTranslation('common') - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // Component state const [characters, setCharacters] = useState>({}) diff --git a/components/reps/GridRep/index.tsx b/components/reps/GridRep/index.tsx index 18439d3c..139c54d5 100644 --- a/components/reps/GridRep/index.tsx +++ b/components/reps/GridRep/index.tsx @@ -1,8 +1,9 @@ +'use client' import React, { useEffect, useState } from 'react' import Link from 'next/link' -import { useRouter } from 'next/router' import { useSnapshot } from 'valtio' import { useTranslation } from 'next-i18next' +import { getCookie } from 'cookies-next' import classNames from 'classnames' import 'fix-date' @@ -31,10 +32,8 @@ const GridRep = ({ party, loading, onClick, onSave }: Props) => { const { account } = useSnapshot(accountState) - const router = useRouter() const { t } = useTranslation('common') - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const [visible, setVisible] = useState(false) const [currentView, setCurrentView] = useState< diff --git a/components/reps/SummonRep/index.tsx b/components/reps/SummonRep/index.tsx index da78510a..613645c2 100644 --- a/components/reps/SummonRep/index.tsx +++ b/components/reps/SummonRep/index.tsx @@ -1,5 +1,6 @@ +'use client' import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import styles from './index.module.scss' @@ -15,9 +16,7 @@ const SUMMONS_COUNT = 4 const SummonRep = (props: Props) => { // Localization for alt tags - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // Component state const [mainSummon, setMainSummon] = useState() diff --git a/components/reps/WeaponRep/index.tsx b/components/reps/WeaponRep/index.tsx index 7bd39bf0..6d429dac 100644 --- a/components/reps/WeaponRep/index.tsx +++ b/components/reps/WeaponRep/index.tsx @@ -1,5 +1,6 @@ +'use client' import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' +import { getCookie } from 'cookies-next' import styles from './index.module.scss' import classNames from 'classnames' @@ -15,9 +16,7 @@ const WEAPONS_COUNT = 9 const WeaponRep = (props: Props) => { // Localization for alt tags - const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // Component state const [mainhand, setMainhand] = useState() diff --git a/components/search/SearchModal/index.tsx b/components/search/SearchModal/index.tsx index 1ccb2112..ffa515b5 100644 --- a/components/search/SearchModal/index.tsx +++ b/components/search/SearchModal/index.tsx @@ -1,6 +1,6 @@ +'use client' import React, { useEffect, useState } from 'react' import { getCookie, setCookie } from 'cookies-next' -import { useRouter } from 'next/router' import { useTranslation } from 'react-i18next' import InfiniteScroll from 'react-infinite-scroll-component' import cloneDeep from 'lodash.clonedeep' @@ -38,9 +38,8 @@ interface Props extends DialogProps { } const SearchModal = (props: Props) => { - // Set up router - const router = useRouter() - const locale = router.locale + // Set up locale from cookie + const locale = (getCookie('NEXT_LOCALE') as string) || 'en' // Set up translation const { t } = useTranslation('common') diff --git a/components/toasts/UpdateToast/index.tsx b/components/toasts/UpdateToast/index.tsx index 4f9f50b5..81f7c428 100644 --- a/components/toasts/UpdateToast/index.tsx +++ b/components/toasts/UpdateToast/index.tsx @@ -1,5 +1,5 @@ +'use client' import React from 'react' -import { useRouter } from 'next/router' import { setCookie } from 'cookies-next' import { add, format } from 'date-fns' import classNames from 'classnames' diff --git a/prd/app-router-migration.md b/prd/app-router-migration.md new file mode 100644 index 00000000..e28dfea9 --- /dev/null +++ b/prd/app-router-migration.md @@ -0,0 +1,209 @@ +# App Router Migration PRD + +## Overview +Complete migration from Next.js Pages Router to App Router by updating all components to use `next/navigation` instead of `next/router`. + +## Migration Requirements + +### Router API Changes +- `useRouter()` from `next/router` → `useRouter()` from `next/navigation` +- `router.query` → `useSearchParams()` hook +- `router.pathname` → `usePathname()` hook +- `router.asPath` → combination of `usePathname()` + `useSearchParams()` +- `router.push()` → stays the same +- `router.replace()` → stays the same +- `router.back()` → stays the same +- `router.reload()` → `router.refresh()` +- `router.prefetch()` → stays the same +- `router.beforePopState()` → not available in App Router (needs alternative) +- `router.events` → not available in App Router (needs alternative) + +### Import Updates Required +All imports need to change from: +```tsx +import { useRouter } from 'next/router' +``` +To: +```tsx +import { useRouter, usePathname, useSearchParams } from 'next/navigation' +``` + +## Files to Migrate + +### App Directory Files (Already using next/navigation - 6 files) +These files already use `next/navigation` but should be verified: +- [ ] `/app/teams/TeamsPageClient.tsx` +- [ ] `/app/saved/SavedPageClient.tsx` +- [ ] `/app/p/[party]/PartyPageClient.tsx` +- [ ] `/app/new/NewPartyClient.tsx` +- [ ] `/app/components/Header.tsx` +- [ ] `/app/[username]/ProfilePageClient.tsx` + +### Pages Directory Files (1 file) +- [ ] `/pages/about.tsx` - Needs migration to app router or update imports + +### Component Files - Auth (3 files) +- [x] `/components/auth/LoginModal/index.tsx` +- [x] `/components/auth/SignupModal/index.tsx` +- [x] `/components/auth/AccountModal/index.tsx` + +### Component Files - Character (5 files) +- [x] `/components/character/CharacterUnit/index.tsx` +- [x] `/components/character/CharacterModal/index.tsx` +- [x] `/components/character/CharacterHovercard/index.tsx` +- [x] `/components/character/CharacterConflictModal/index.tsx` +- [x] `/components/character/CharacterResult/index.tsx` + +### Component Files - Weapon (7 files) +- [x] `/components/weapon/WeaponUnit/index.tsx` +- [x] `/components/weapon/WeaponModal/index.tsx` +- [x] `/components/weapon/WeaponKeySelect/index.tsx` +- [x] `/components/weapon/WeaponHovercard/index.tsx` +- [x] `/components/weapon/WeaponConflictModal/index.tsx` +- [x] `/components/weapon/WeaponResult/index.tsx` +- [x] `/components/weapon/WeaponLabelIcon/index.tsx` + +### Component Files - Summon (3 files) +- [x] `/components/summon/SummonUnit/index.tsx` +- [x] `/components/summon/SummonHovercard/index.tsx` +- [x] `/components/summon/SummonResult/index.tsx` + +### Component Files - Party (5 files) +- [x] `/components/party/PartyHeader/index.tsx` +- [x] `/components/party/PartyHead/index.tsx` +- [x] `/components/party/PartyFooter/index.tsx` +- [x] `/components/party/PartyDropdown/index.tsx` +- [x] `/components/party/Party/index.tsx` + +### Component Files - Job (7 files) +- [x] `/components/job/JobSkillItem/index.tsx` +- [x] `/components/job/JobSection/index.tsx` +- [x] `/components/job/JobDropdown/index.tsx` +- [x] `/components/job/JobAccessoryPopover/index.tsx` +- [x] `/components/job/JobAccessoryItem/index.tsx` +- [x] `/components/job/JobSkillResult/index.tsx` +- [x] `/components/job/JobImage/index.tsx` + +### Component Files - Mastery (3 files) +- [x] `/components/mastery/ExtendedMasterySelect/index.tsx` +- [x] `/components/mastery/AxSelect/index.tsx` +- [x] `/components/mastery/AwakeningSelectWithInput/index.tsx` + +### Component Files - Reps (4 files) +- [x] `/components/reps/GridRep/index.tsx` +- [x] `/components/reps/CharacterRep/index.tsx` +- [x] `/components/reps/WeaponRep/index.tsx` +- [x] `/components/reps/SummonRep/index.tsx` + +### Component Files - Common (2 files) +- [x] `/components/common/SelectWithInput/index.tsx` +- [x] `/components/common/Editor/index.tsx` + +### Component Files - Extra (2 files) +- [x] `/components/extra/GuidebookUnit/index.tsx` +- [x] `/components/extra/GuidebookResult/index.tsx` + +### Component Files - Other (11 files) +- [x] `/components/toasts/UpdateToast/index.tsx` +- [x] `/components/search/SearchModal/index.tsx` +- [x] `/components/raids/RaidCombobox/index.tsx` +- [x] `/components/filters/FilterModal/index.tsx` +- [x] `/components/MentionList/index.tsx` +- [x] `/components/Layout/index.tsx` +- [x] `/components/LanguageSwitch/index.tsx` +- [x] `/components/Header/index.tsx` +- [x] `/components/ElementToggle/index.tsx` +- [x] `/components/about/ChangelogUnit/index.tsx` +- [x] `/components/HovercardHeader/index.tsx` + +### Utility Files (1 file) +- [x] `/utils/changeLanguage.tsx` + +## Total Files to Migrate: 59 + +## Migration Steps for Each File + +1. **Analyze current usage** + - Check how `router` is being used + - Identify which properties/methods are accessed + - Note any router.events listeners + +2. **Update imports** + - Change from `next/router` to `next/navigation` + - Add additional hooks if needed (`usePathname`, `useSearchParams`) + +3. **Update router usage** + - Replace `router.query` with `useSearchParams()` + - Replace `router.pathname` with `usePathname()` + - Update any other router property access + +4. **Handle edge cases** + - Router events (need alternative approach) + - beforePopState (need alternative approach) + - Dynamic route params (use `useParams()`) + +5. **Test the component** + - Ensure navigation still works + - Check query parameter handling + - Verify dynamic routes + +## Special Considerations + +### Router Events +Files using `router.events` will need special handling as this is not available in App Router. Common patterns: +- Route change start/complete events → Use loading.tsx or Suspense +- Route change error → Use error boundaries + +### Query Parameters +When migrating `router.query`: +```tsx +// Old (Pages Router) +const { id, filter } = router.query + +// New (App Router) +const searchParams = useSearchParams() +const id = searchParams.get('id') +const filter = searchParams.get('filter') +``` + +### Dynamic Route Parameters +For dynamic segments like `[id]`: +```tsx +// Old (Pages Router) +const { id } = router.query + +// New (App Router) +import { useParams } from 'next/navigation' +const params = useParams() +const id = params.id +``` + +### Programmatic Navigation +```tsx +// Both routers (mostly the same) +router.push('/path') +router.replace('/path') +router.back() + +// Refresh (different) +// Old: router.reload() +// New: router.refresh() +``` + +## Post-Migration Cleanup + +After all files are migrated: +1. [ ] Remove `/pages` directory (except `/pages/api` if still needed) +2. [ ] Remove Pages Router specific configuration +3. [ ] Update `_app.tsx` dependencies if any remain +4. [ ] Test all routes thoroughly +5. [ ] Update any documentation + +## Success Criteria + +- [x] All components use `next/navigation` imports +- [x] No references to `next/router` remain in components (only in pages/about.tsx which still needs migration) +- [x] All navigation functionality works as before +- [x] No console errors about "NextRouter was not mounted" +- [x] App runs successfully with `npm run dev` +- [ ] Build completes with `npm run build` (to be tested) \ No newline at end of file