Question
I am new to React and working on a project that uses TypeScript. I have noticed both .ts and .tsx file extensions, and I am not sure when each one should be used.
Since both appear in React projects, where should I use .ts and where should I use .tsx?
Short Answer
By the end of this page, you will understand the difference between .ts and .tsx files in TypeScript projects, especially in React. You will learn when to use each extension, why the distinction matters, and how developers typically organize these files in real codebases.
Concept
TypeScript uses different file extensions to tell the compiler what kind of syntax may appear inside a file.
.tsmeans a regular TypeScript file.tsxmeans a TypeScript file that may contain JSX
JSX is the HTML-like syntax commonly used in React, such as:
const element = <h1>Hello</h1>;
That syntax is not plain TypeScript. Because of that, TypeScript needs a special file extension: .tsx.
When to use .ts
Use .ts for files that contain TypeScript code without JSX.
Examples:
- utility functions
- API helpers
- data transformation logic
- type definitions
- custom hooks that do not return JSX
- constants and configuration
Example:
export function add(a: number, b: number): number {
return a + b;
}
When to use .tsx
Use .tsx for files that contain JSX, which is usually React component markup.
Example:
export function Welcome() {
return <h1>Welcome</h1>;
}
Why this matters
The extension helps the TypeScript compiler and editor understand how to parse the file.
If you put JSX inside a .ts file, TypeScript will usually throw a syntax error because it does not expect JSX there.
In real programming, choosing the correct extension makes your code:
- compile correctly
- easier to read
- easier to maintain
- more consistent for teams
A simple rule is:
- No JSX ->
.ts - Contains JSX ->
.tsx
Mental Model
Think of the file extension as a label on a box.
- A
.tsbox says: This box contains TypeScript only - A
.tsxbox says: This box may contain TypeScript plus React-style markup
If you try to put JSX markup into a .ts box, the compiler gets confused because the label says it should only expect regular TypeScript.
So the extension is not just naming style. It tells the tools how to read the file.
Syntax and Examples
Here is the basic idea.
.ts example
Use .ts for logic-only files:
// math.ts
export function multiply(a: number, b: number): number {
return a * b;
}
This file contains only TypeScript. There is no JSX.
.tsx example
Use .tsx for React components that render JSX:
// Button.tsx
type ButtonProps = {
label: string;
};
export function Button({ label }: ButtonProps) {
return <button>{label}</button>;
}
This file must be .tsx because it returns JSX.
Another practical example
A React project often separates UI from logic:
// formatPrice.ts
export function formatPrice(price: number): string {
return `$${price.toFixed(2)}`;
}
// ProductCard.tsx
import { formatPrice } from './formatPrice';
type ProductCardProps = {
name: string;
price: number;
};
export function ProductCard({ name, price }: ProductCardProps) {
return (
<div>
<h2>{name}</h2>
<p>{formatPrice(price)}</p>
</div>
);
}
The formatting helper is .ts, while the component is .tsx because it contains JSX.
Step by Step Execution
Consider this file:
type Props = {
name: string;
};
export function Greeting({ name }: Props) {
return <h1>Hello, {name}</h1>;
}
Here is what happens step by step:
-
type Props = { name: string; };- A TypeScript type is created.
- It says the component expects one prop called
name, and it must be a string.
-
export function Greeting({ name }: Props)- A function component is declared.
- The parameter is checked against the
Propstype.
-
return <h1>Hello, {name}</h1>;- This returns JSX.
- Because JSX is present, the file must use the
.tsxextension.
-
TypeScript and the React tooling parse the file as TSX.
- The JSX is transformed into JavaScript that the browser can eventually run.
Now compare it with this file:
export function greet(name: string): string {
return `Hello, ${name}`;
}
Step by step:
- A normal TypeScript function is declared.
- It accepts a string.
- It returns a string.
- There is no JSX anywhere.
- The correct extension is
.ts.
The key difference is not whether the file belongs to a React project. The key difference is whether the file contains JSX.
Real World Use Cases
Here are common real-world cases.
Use .ts for
- API clients
- functions that call
fetchoraxios
- functions that call
- utility functions
- date formatting, math helpers, string cleanup
- type definitions
- shared interfaces and type aliases
- state logic without JSX
- reducers, selectors, validation rules
- configuration files
- route config, environment parsing, constants
- custom hooks without JSX
- hooks that only return values and functions
Example:
export async function getUsers() {
const response = await fetch('/api/users');
return response.json();
}
Use .tsx for
- React components
- pages, buttons, modals, cards
- layout components
- wrappers that return JSX structure
- components with conditional rendering
- loading states, error states, empty states
- files that embed JSX in variables
const content = <span>Done</span>
Example:
export function LoadingMessage() {
return <p>Loading...</p>;
}
Real Codebase Usage
In real projects, developers usually keep a clean separation between presentation and logic.
Common pattern
.tsxfiles handle rendering.tsfiles handle logic, data shaping, and reusable helpers
Example structure:
src/
components/
UserCard.tsx
Navbar.tsx
hooks/
useUsers.ts
utils/
formatDate.ts
validateEmail.ts
types/
user.ts
api/
users.ts
Typical usage patterns
Guard clauses in .tsx
Components often use guard clauses before rendering:
type Props = {
isLoading: boolean;
name?: string;
};
export function ProfileHeader({ isLoading, name }: Props) {
if (isLoading) return <p>Loading...</p>;
if (!name) return <p>No user found</p>;
return <h1>{name}</h1>;
}
Validation and helpers in .ts
Validation logic is often kept outside components:
export function isValidEmail(email: string): boolean {
return email.includes('@');
}
Reusable types in .ts
Many teams place shared types in .ts files:
export type User = {
id: number;
name: string;
};
Hooks can be either .ts or .tsx
A custom hook is usually .ts if it only uses React hooks and returns data:
import { useEffect, useState } from 'react';
export function useCounter() {
const [count, setCount] = useState(0);
return { count, setCount };
}
But if a hook file somehow contains JSX, it must be .tsx. That is less common.
Practical team rule
A common team convention is:
- default to
.ts - switch to
.tsxonly when JSX appears
That keeps the codebase clear and avoids unnecessary TSX files.
Common Mistakes
Here are some common beginner mistakes.
1. Using .ts for a component that returns JSX
Broken example:
export function App() {
return <h1>Hello</h1>;
}
Why it fails:
- The file contains JSX.
.tsfiles are not meant to parse JSX.
Fix:
- Rename the file to
.tsx
export function App() {
return <h1>Hello</h1>;
}
2. Assuming all React files must be .tsx
This is not necessary.
Example:
import { useState } from 'react';
export function useToggle() {
const [value, setValue] = useState(false);
return { value, setValue };
}
This can stay as .ts because there is no JSX.
3. Putting types in .tsx when they do not need JSX
This is not wrong, but it can make the project less organized.
Better:
export type Product = {
id: number;
name: string;
};
Keep pure type files in .ts.
4. Confusing TypeScript angle brackets with JSX
Some syntax can look similar.
Example:
const value = someFunction<string>();
In a .tsx file, certain angle-bracket forms can be ambiguous because JSX also uses < >.
A common workaround is to prefer other TypeScript syntax where needed.
5. Renaming everything to .tsx
This usually works if the code is valid, but it removes the useful distinction between logic files and JSX files.
A better habit is:
- use
.tsby default - use
.tsxonly when JSX is present
Comparisons
Here is the practical comparison.
| Extension | Meaning | Can contain JSX? | Common use |
|---|---|---|---|
.ts | Regular TypeScript file | No | utilities, types, hooks, API logic |
.tsx | TypeScript + JSX file | Yes | React components and UI files |
.ts vs .tsx
| Question | .ts | .tsx |
|---|---|---|
| Used in TypeScript projects? | Yes | Yes |
| Used in React projects? | Yes | Yes |
| Supports JSX syntax? | No | Yes |
| Best for UI components? | No | Yes |
| Best for helpers and types? | Yes | Usually not needed |
Important clarification
The difference is not:
- React project vs non-React project
- simple file vs complex file
The real difference is:
- JSX absent ->
.ts - JSX present ->
.tsx
Cheat Sheet
- Use
.tsfor TypeScript files with no JSX - Use
.tsxfor TypeScript files that contain JSX - React components that return JSX are usually
.tsx - Utility files, type files, API files, and many hooks are usually
.ts
Quick rule
No JSX -> .ts
Has JSX -> .tsx
Examples
export function sum(a: number, b: number) {
return a + b;
}
export function Header() {
return <header>Site Header</header>;
}
Common file types
Button.tsx-> componentUserList.tsx-> componentformatDate.ts-> helperapi.ts-> API logictypes.ts-> typesuseAuth.ts-> hook without JSX
Edge case
A file can import React-related code and still remain .ts if it does not contain JSX.
import { useEffect } from 'react';
That alone does not require .tsx.
FAQ
What is .tsx in TypeScript?
.tsx is a TypeScript file extension used for files that contain JSX, which is commonly used in React components.
What is the difference between .ts and .tsx?
.ts is for regular TypeScript code without JSX. .tsx is for TypeScript code that includes JSX.
Should all React files be .tsx?
No. Only files that contain JSX need .tsx. Logic-only files in a React project can stay as .ts.
Can I use hooks in a .ts file?
Yes. If the file uses hooks like useState or useEffect but does not return JSX, .ts is fine.
Can I write a React component in a .ts file?
Only if it does not contain JSX. In practice, most React components return JSX, so they should usually be .tsx.
What happens if I put JSX in a .ts file?
TypeScript will usually show parsing or syntax errors because .ts files are not expected to contain JSX.
Are type definition files .ts or .tsx?
Usually .ts, because they normally contain only types and no JSX.
Mini Project
Description
Create a small React TypeScript feature that separates UI code from logic code. This project demonstrates exactly when to use .ts and when to use .tsx by building a product card component that uses a helper function from a separate file.
Goal
Build a React component in .tsx that displays product information and uses a .ts utility file to format the price.
Requirements
Requirement 1 Requirement 2 Requirement 3
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.