Building the Template Page
MARCH 11, 2026 | JOHN NYINGI
📕In this chapter we will build the Template Page, which we will customize later.
Table of Content
Building the Template Page
Template UI
We need some ShadCN components for this page, namely, Card and Button. Run the command below to add these two components.
npx shadcn@latest add Button Card
We will need a profile image placeholder for the card. Feel free to pick one from pexels.com or use your own. Copy the image to the src/assets folder, and we will import it to our component.
Next, we will need some placeholder data; our template cannot be blank. Placeholder data offers a preview to our users. So let’s define a structure that will hold our data.
Let’s create a new folder types inside src, inside this types folder, create a file morph.ts, we will use this file to save our “Models” or Schemas. For this case, let’s define our schema, which will hold our data.
export interface MorphData {
imageUrl?: string;
name: string;
description: string;
email?: string;
mobileno?: string;
theme?: {
titleFont: string;
contentFont: string;
}
}Create a service folder inside the src folder. Now, create a service file named morphService.ts. This file will contain our “business logic”. In this file, let’s create our DEFAULT_DATA which will be the placeholder data.
import type { MorphData } from "@/types/morph";
const DEFAULT_DATA: MorphData = {
name: "Jane Doe",
description: "I am a physio therapist with over 10 years of experience. I specialize in sports injuries and rehabilitation. I am passionate about helping my patients recover and get back to their active lifestyles.",
theme: {
titleFont: "font-nunito",
contentFont: "font-opensans",
},
email: "jane.doe@example.com",
mobileno: "+1234567890",
}
export const getInitialData = (): MorphData => {
return DEFAULT_DATA;
}At the bottom, you can see we have added a function. getInitialData This is the service we will call to fetch our data.
Let’s get started with building the Template Page. We start by creating the pages folder inside the src/ folder. In it, create a file ViewPage.tsx, let’s include the profile picture you saved in assets and our default data.
To use default data, it’s necessary that we store it in our component as a state, and to avoid calling the function multiple times.
import { useState } from "react";
import { type MorphData } from "@/types/morph";
import profileImage from "@/assets/profile-picture.jpg";
const ViewPage = () => {
const [data, setData] = useState<MorphData>(() => getInitialData(cardID));
return (
<main className="flex min-h-screen w-full items-center justify-center px-2 bg-linear-to-b from-[#c9bed8] to-[#676ef1]">
<Card id="print-area" className="md:w-lg mx-auto bg-gray-800 text-white pb-0 shadow-2xl">
<CardHeader>
<CardTitle className="flex flex-col gap-3">
<div className="flex justify-start">
<img src={data?.imageUrl ?? profileImage} alt="profile image" className="rounded-full h-32 w-32 object-cover border-[6px] border-amber-500" />
</div>
<h1 className={`text-2xl md:text-5xl font-bold ${data?.theme?.titleFont ?? "font-opensans"}`}>{data.name}</h1>
</CardTitle>
</CardHeader>
<CardContent className={`text-md ${data?.theme?.contentFont ?? "font-opensans"}`}>
{data.description}
</CardContent>
<CardFooter className="grid grid-cols-2 md:grid-cols-8 md:gap-2 gap-4 bg-gray-900/40 py-4">
<div className="col-span-4 flex items-center gap-1 px:0 bg-inherit">
<MailIcon className="size-6 text-amber-500" />
<span className="ml-2">{data.email}</span>
</div>
<div className="col-span-4 flex items-center gap-1 px:0 bg-inherit">
<PhoneIcon className="size-6 text-amber-500" />
<span className="ml-2">{data.mobileno}</span>
</div>
</CardFooter>
</Card>
</main>
);
};
export default ViewPageWhat is useState? This is a react Hook that allows you to add state variables to functional components. It’s essentially a way for a component to “remember” information between states.
You’ll also notice we have null checks.
Null Coalescing (??): For the data.imageUrl if the data or imageUrl is null or undefined, the check will fall back to profileImage
data?.imageUrl ?? profileImage // if data.imageUrl is null/undefined use profileImage else use itYou’ll also notice we are using our custom fonts font-opensans which we defined in our previous chapter.
Now, let’s add our ViewPage.tsx to our Routes so that we can navigate to it. Open App.tsx and add the below route. Make sure to import the component.
<Router>
<Routes>
<Route path='/' element={<ViewPage />} />
</Routes>
</Router>Open the terminal and run the command below.
npm run devThe console will indicate the URL path and port on which the app is running. Click the link or open it in your browser. Your page should look like this;
We’re done with the first part of the ViewPage.tsx implementation; we will revisit it later to add more functionality.
Up next, we will build the Design page, which will contain our editor and renderer. To continue with this series and get the full source code, consider buying the article using the link below.
follow me on X @jmost_



