Spacing or Padding between header and <ion-router-outlet>

Quick question here guys, I cant seem to figure this out. I have a nested router inside of a component. My main routes are contained in my App.tsx file as shown here.

const App: React.FC = () => (
  <IonApp>
    <IonReactRouter>
      <IonTabs>
        <IonRouterOutlet>
          <Route exact path="/events">
            <Tab1 />
          </Route>
          <Route path="/preferences">
            <Preferences />
          </Route>
          <Route path="/tab3">
            <Tab3 />
          </Route>
          <Route exact path="/">
            <Redirect to="/events" />
          </Route>
        </IonRouterOutlet>
        <IonTabBar slot="bottom">
          <IonTabButton tab="tab1" href="/events">
            <IonIcon icon={calendar} />
            <IonLabel>Events</IonLabel>
          </IonTabButton>
          <IonTabButton tab="tab2" href="/preferences">
            <IonIcon icon={reader} />
            <IonLabel>Preferences</IonLabel>
          </IonTabButton>
          <IonTabButton tab="tab3" href="/tab3">
            <IonIcon icon={cog} />
            <IonLabel>Profile</IonLabel>
          </IonTabButton>
        </IonTabBar>
      </IonTabs>
    </IonReactRouter>
  </IonApp>
);

Inside of this I have a Preferences.tsx component. In this component I have another nested router setup. Routing works fine, its just the content that’s loaded in my router when rendered, is overlapped by my IonHeader thats in my preferences.tsx component. Heres the code, and an image of what it looks like. Any suggestions?

Preferences.tsx

<IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>Preferences</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent fullscreen className="ion-padding">
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Preferences</IonTitle>
          </IonToolbar>
        </IonHeader>
        <IonRouterOutlet>
          <Route exact path='/preferences/preference-list'>
            <PreferenceList/>
          </Route>
          <Route path='/preferences/preference-slides'>
            <PreferenceSlides/>
          </Route>
          <Route exact path="/preferences">
            <Redirect to="/preferences/preference-list" />
          </Route>
        </IonRouterOutlet>
      </IonContent>
    </IonPage>

and here is an image of what the rendering looks like

In Preferences.tsx, I would render an IonPage for each individual route:

<IonPage>
        <IonRouterOutlet>
          <Route exact path='/preferences/preference-list'>
            <IonPage>
              <PreferenceList/>
             </IonPage>
          </Route>
          <Route path='/preferences/preference-slides'>
            <IonPage>
              <PreferenceSlides/>
             </IonPage>
          </Route>
          <Route exact path="/preferences">
            <Redirect to="/preferences/preference-list" />
          </Route>
        </IonRouterOutlet>
</IonPage>

I am guessing that sharing the header among multiple pages is breaking things; even if the header text is the same, I would add it separately to each .

I’m using something similar. My project configuration is with Vue 3 and typesript.

<template>
  <ion-app id="app">
    <AppHeader ref="header" />
    <ion-content :class="roterOutlet">
      <ion-router-outlet />
    </ion-content>
    <AppFooter ref="footer" />
  </ion-app>
</template>

<script lang="ts">
import { IonApp, IonContent, IonRouterOutlet } from "@ionic/vue";
import { defineComponent } from "vue";
import { useResizeObserver } from "@vueuse/core";
import { store } from "@/modules/store";
import { IElementDimensions } from "@/interfaces/interfaces";
import AppHeader from "@/components/AppHeader.vue";
import AppFooter from "@/components/AppFooter.vue";

export default defineComponent({
  name: "App",
  components: {
    IonApp,
    IonRouterOutlet,
    AppHeader,
    AppFooter,
    IonContent,
  },
  computed: {
    roterOutlet() {
      const {width, height}: IElementDimensions = store.getters["appManagement/getHeaderDimensions"];
      
      return `padding-top: ${height}px;`      
    }
  },
  mounted() {
    useResizeObserver(this.$refs.header as HTMLElement, ([v]) => {
      if (v.contentRect) {
        const dimensions: IElementDimensions = {
          height: v.contentRect.height!,
          width: v.contentRect.width!,
        };
        (async () => {
          await store.dispatch(
            "appManagement/saveHeaderDimensions",
            dimensions
          );
        })();
      }
    });
    useResizeObserver(this.$refs.footer as HTMLElement, ([v]) => {
      if (v.contentRect) {
        const dimensions: IElementDimensions = {
          height: v.contentRect.height!,
          width: v.contentRect.width!,
        };
        (async () => {
          await store.dispatch(
            "appManagement/saveFooterDimensions",
            dimensions
          );
        })();
      }
    });
  },
});
</script>

The whole idea is to get the height of the header and set padding-top: {height}px; to ion-content that is wrapping ion-router-outlet. there are different ways to get the height dynamically and this example is very vue specific. Please don’t ask me why the dimension values are 1st saved and 2nd read from vue store. Client wish.