The fastest way to call a component in React is to render it via JSX and pass strictly typed props. This article shows how to call a component in React with TypeScript and Zod so you get compile-time and runtime safety, clear state management, and production-ready patterns.
The Problem
Developers often "call" (render) a component without strict typing or validation, leading to runtime bugs, unclear state, and hard-to-test UI.
Prerequisites
Node.js 20+, pnpm or npm, React 19, TypeScript 5+, Zod 3+, a modern browser. Ensure tsconfig has strict: true.
The Solution (Step-by-Step)
Step 1: Bootstrap a minimal TypeScript + React app
// package.json (excerpt) - ensures React 19 and strict TS
{
"name": "react-call-component-ts",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"zod": "^3.22.0"
},
"devDependencies": {
"typescript": "^5.6.0",
"vite": "^5.0.0",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0"
}
}
// tsconfig.json - strict mode enabled for maximum safety
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM"],
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"skipLibCheck": true
},
"include": ["src"]
}
Step 2: Create a strictly typed child component with runtime validation
// src/components/Greeting.tsx
import React, { memo } from "react";
import { z } from "zod";
// 1) Define compile-time props shape via TypeScript
export type GreetingProps = {
name: string; // Required user name
mode: "friendly" | "formal"; // Discriminated literal union for behavior
};
// 2) Define runtime schema using Zod for additional safety in production
const greetingPropsSchema = z.object({
name: z.string().min(1, "name is required"),
mode: z.union([z.literal("friendly"), z.literal("formal")])
});
// 3) React.memo to avoid unnecessary re-renders when props are stable
export const Greeting = memo(function Greeting(props: GreetingProps) {
// Validate props at runtime to fail fast in dev and log issues in prod
const result = greetingPropsSchema.safeParse(props);
if (!result.success) {
// Render a small fallback and log schema errors for debugging
console.error("Greeting props invalid:", result.error.format());
return Invalid greeting config;
}
// Safe, parsed props
const { name, mode } = result.data;
// Render based on discriminated union value
if (mode === "friendly") {
return Hi, {name}! Welcome back.
;
}
return Hello, {name}. It is good to see you.
;
});
Explanation: We "call" a component in React by placing it in JSX like <Greeting name="Sam" mode="friendly" />. The TypeScript type enforces correct usage at compile time; Zod enforces it at runtime.
Step 3: Manage parent state with discriminated unions and render the child
// src/App.tsx
import React, { useEffect, useState } from "react";
import { Greeting } from "./components/Greeting";
// Discriminated union for page state: guarantees exhaustive checks
type PageState =
| { kind: "loading" }
| { kind: "ready"; userName: string }
| { kind: "error"; message: string };
export function App() {
const [state, setState] = useState({ kind: "loading" });
// Simulate fetching the current user, then set ready state
useEffect(() => {
const timer = setTimeout(() => {
// In a real app, replace with a fetch call and proper error handling
setState({ kind: "ready", userName: "Sam" });
}, 300);
return () => clearTimeout(timer);
}, []);
// Render different UI based on discriminated union state
if (state.kind === "loading") {
return Loading…
;
}
if (state.kind === "error") {
return Error: {state.message}
;
}
// Key line: this is how you "call" (render) the component with props
return (
Dashboard
{/* Rendering a list of components safely */}
{(["Ada", "Linus", "Grace"] as const).map((n) => (
))}
);
}
Step 4: Mount the app
// src/main.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
const container = document.getElementById("root");
if (!container) throw new Error("Root container missing");
createRoot(container).render(
// StrictMode helps surface potential issues
);
Best Practices & Security
Pro-Tip: Use React.memo for presentational components to avoid unnecessary re-renders.
Pro-Tip: Use discriminated unions for UI state to guarantee exhaustive handling and safer refactors.
Pro-Tip: Validate at runtime with Zod for boundary inputs (API responses, query params, environment-driven config).
Pro-Tip: Prefer useCallback and stable prop shapes when passing callbacks to memoized children.
Pro-Tip: Keep components pure; avoid hidden side effects inside render logic.
Security note (front-end): Do not embed secrets in the client. If you integrate with Azure or any backend, call a secured API instead of accessing resources directly from the browser.
Security note (Azure backend integration): Use Managed Identity and DefaultAzureCredential in the server/API, not the frontend. Grant the server's managed identity least-privilege RBAC roles only. Example: for Azure Storage reads, assign Storage Blob Data Reader to the API identity at the specific container scope.
Security note (data flow): Validate user input and API responses at the edge (API) with Zod or similar, then keep the front-end strictly typed.
Summary
• You call a component in React by rendering it in JSX with strictly typed, validated props.
• Discriminated unions make UI state predictable, and React.memo boosts performance.
• For real backends, keep secrets server-side, use Managed Identity with least-privilege RBAC, and validate at the edge.
Testing Quickstart
Test a component render with React Testing Library
// src/components/Greeting.test.tsx
import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { Greeting } from "./Greeting";
test("renders friendly greeting", () => {
render(<Greeting name="Sam" mode="friendly" />);
expect(screen.getByText(/Hi, Sam!/)).toBeInTheDocument();
});
test("renders formal greeting", () => {
render(<Greeting name="Ada" mode="formal" />);
expect(screen.getByText(/Hello, Ada\./)).toBeInTheDocument();
});
This test verifies the component is "called" with valid props and renders deterministic output. For invalid props, assert that the fallback appears and console error is triggered.