Skip to content
touchcn
Esc
navigateopen⌘Jpreview
On this page

Page

A page scaffold with scroll restoration.

A page scaffold — the outer container for a screen, with scroll restoration handled by the engine.

Installation

npx touchcn add page

Usage

<tcn-page>
  <tcn-navbar title="Home" />
  <!-- page content -->
</tcn-page>
import { TcnPage } from '@/components/ui/page';
import { TcnNavbar } from '@/components/ui/navbar';

<TcnPage>
  <TcnNavbar title="Home" />
  {/* page content */}
</TcnPage>
<script setup lang="ts">
import { TcnPage } from '@/components/ui/page';
import { TcnNavbar } from '@/components/ui/navbar';
</script>

<template>
  <TcnPage>
    <TcnNavbar title="Home" />
    <!-- page content -->
  </TcnPage>
</template>

Props

TcnPage forwards all standard <div> attributes. Place a tcn-navbar and tcn-tabbar inside it near the app root so their position: fixed chrome anchors to the viewport.

Source · Angular
export * from './tcn-page';
import { Component, DestroyRef, ElementRef, inject } from '@angular/core';
import { TouchcnScrollRestoration } from '@touchcn/angular';

@Component({
  selector: 'tcn-page',
  template: '<ng-content />',
  host: {
    class:
      'tcn-page tcn-safe-x relative block h-dvh overflow-y-auto bg-[var(--color-surface)] text-[var(--color-on-surface)]',
  },
})
export class TcnPage {
  private readonly scrollRestoration = inject(TouchcnScrollRestoration, { optional: true });

  constructor() {
    const element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;
    this.scrollRestoration?.register(element);
    inject(DestroyRef).onDestroy(() => this.scrollRestoration?.unregister(element));
  }
}
Source · React
export * from './tcn-page';
import { forwardRef } from 'react';
import type { ComponentPropsWithoutRef } from 'react';
import { cn } from '@touchcn/core';

export type TcnPageProps = ComponentPropsWithoutRef<'div'>;

/**
 * Full-height, scrollable screen container. It fills the dynamic viewport
 * (`h-dvh`), owns the vertical scroll, and applies the horizontal safe-area
 * insets so content never sits under a notch.
 */
export const TcnPage = forwardRef<HTMLDivElement, TcnPageProps>(function TcnPage({ className, children, ...props }, ref) {
  return (
    <div
      ref={ref}
      className={cn(
        'tcn-page tcn-safe-x relative block h-dvh overflow-y-auto bg-[var(--color-surface)] text-[var(--color-on-surface)]',
        className,
      )}
      {...props}
    >
      {children}
    </div>
  );
});
Source · Vue
<template>
  <div
    class="tcn-page tcn-safe-x relative block h-dvh overflow-y-auto bg-[var(--color-surface)] text-[var(--color-on-surface)]"
  >
    <slot />
  </div>
</template>
export { default as TcnPage } from './TcnPage.vue';

Last updated on July 24, 2026

Was this page helpful?