reactjsionic-framework

Error: An Ionic Router is required for IonRouterContext


I am using Ionic Framework, and Firebase in a React and Typescript project and coming across the error 'An Ionic Router is required for IonRouterContext' when trying to navigate after successful auth login.

I have tried removing the AppRoutes file and having it all in App.tsx but still experiencing the error.

App.tsx

const App: React.FC = () => {
    return (
        <IonApp>
            <IonReactRouter>
                <IonRouterOutlet>
                    <AppRoutes />
                </IonRouterOutlet>
            </IonReactRouter>
        </IonApp>
    )
}

export default App

appRoutes.tsx

import React from 'react'
import { Route } from 'react-router-dom'
import { IonRouterOutlet } from '@ionic/react'

import Dashboard from '@/pages/dashboard'

import SignUp from '@/components/onboarding/signup'
import Login from '@/components/onboarding/login'
import Landing from '@/components/onboarding/landing'

const AppRoutes: React.FC = () => {
    return (
        <IonRouterOutlet>
            <Route exact path="/" component={Landing} />
            <Route exact path="/onboarding" component={Landing} />
            <Route exact path="/signup" component={SignUp} />
            <Route exact path="/login" component={Login} />
            <Route exact path="/dashboard" component={Dashboard} />
        </IonRouterOutlet>
    )
}

export default AppRoutes

landing.tsx (the routes here to go to the login and signup components work)

import React from 'react'
import { IonContent, IonButton, IonPage, IonNavLink } from '@ionic/react'

import '@/styles/onboarding/landing.scss'
import SignUp from './signup'
import Login from './login'

const Landing: React.FC = () => {
    return (
        <>
            <IonContent fullscreen>
                <div className="container">
                    <div className="buttons ion-float-bottom">
                        <IonNavLink routerDirection="forward" component={() => <SignUp />}>
                            <IonButton className="signup-btn" fill="solid" expand="block">
                                Create an account
                            </IonButton>
                        </IonNavLink>
                        <IonNavLink routerDirection="forward" component={() => <Login />}>
                            <IonButton className="login-btn" fill="outline" expand="block">
                                Sign In
                            </IonButton>
                        </IonNavLink>
                    </div>
                </div>
            </IonContent>
        </>
    )
}

export default Landing

login.tsx

import React, { useState } from 'react'
import { IonPage, IonContent, IonHeader, IonInput, IonButton, IonItem, IonButtons, IonBackButton, useIonRouter } from '@ionic/react'
import { loginUser } from '@/auth/firebase'

import '@/styles/onboarding/login.scss'

const Login: React.FC = () => {
    const router = useIonRouter()

    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [error, setError] = useState<string | null>(null)

    const handleLogin = async () => {
        try {
            const user = await loginUser(email, password)

            if (user) {
                // Login successful, redirect to dashboard
                router.push('/dashboard')
            } else {
                setError('Invalid email or password')
            }
        } catch (error) {
            console.log(`---error:`, error)

            setError('Error logging in')
        }
    }

    return (
        <IonPage className="login-page">
            <IonHeader>
                <IonButtons slot="start">
                    <IonBackButton defaultHref="/" />
                </IonButtons>
            </IonHeader>
            <IonContent className="ion-padding">
                <IonItem>
                    <IonInput type="email" placeholder="Email" value={email} onIonChange={e => setEmail(e.detail.value!)} />
                </IonItem>
                <IonItem>
                    <IonInput type="password" placeholder="Password" value={password} onIonChange={e => setPassword(e.detail.value!)} />
                </IonItem>

                {error && <p style={{ color: 'red' }}>{error}</p>}

                <IonButton id="login-button" expand="block" onClick={handleLogin}>
                    Login
                </IonButton>
            </IonContent>
        </IonPage>
    )
}

export default Login

router.push('/dashboard') is the problem here and is giving me that error. Is my structure incorrect?


Solution

  • 1 Remove the Nested IonRouterOutlet: You should only have one IonRouterOutlet in your app. The IonRouterOutlet should wrap the routes defined in AppRoutes.

    2 Correct App Structure: Here’s how you can modify your App.tsx and AppRoutes.tsx to eliminate the nested IonRouterOutlet:

    App.tsx:

    import React from 'react';
    import { IonApp } from '@ionic/react';
    import { IonReactRouter } from '@ionic/react-router';
    import AppRoutes from './AppRoutes';
    
    const App: React.FC = () => {
    return (
        <IonApp>
            <IonReactRouter>
                <AppRoutes />
            </IonReactRouter>
        </IonApp>
    );
    }
    
    export default App;
    

    AppRoutes.tsx:

    import React from 'react';
    import { Route } from 'react-router-dom';
    import { IonRouterOutlet } from '@ionic/react';
    import { IonReactRouter } from '@ionic/react-router';
    
    import Dashboard from '@/pages/dashboard';
    import SignUp from '@/components/onboarding/signup';
    import Login from '@/components/onboarding/login';
    import Landing from '@/components/onboarding/landing';
    
    const AppRoutes: React.FC = () => {
    return (
        <IonRouterOutlet>
            <Route exact path="/" component={Landing} />
            <Route exact path="/onboarding" component={Landing} />
            <Route exact path="/signup" component={SignUp} />
            <Route exact path="/login" component={Login} />
            <Route exact path="/dashboard" component={Dashboard} />
        </IonRouterOutlet>
    );
    }
    
    export default AppRoutes;
    

    Summary of Changes:

    1. Removed the extra IonRouterOutlet in AppRoutes.tsx. The IonRouterOutlet in App.tsx now directly contains the routing logic.

    2. Ensure that IonReactRouter only wraps the main routing outlet, making it available for navigation.