TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
164: TypeScript and JSX Configuration
If you've spent any time in a React codebase, you've probably noticed a weird redundancy: every single file that contains a component starts with import React from 'react';. For years, we just accepted this as the cost of doing business. If you forgot that line, the compiler would scream at you because it didn't know how to turn your JSX tags into actual JavaScript function calls. This is exactly where the jsx configuration in your tsconfig.json comes into play.
The manual import headache
The naive way to handle JSX in TypeScript is by setting "jsx": "react". In this mode, TypeScript treats JSX as a direct call to React.createElement(). If you're building a UserDashboard component, the compiler literally replaces your <div> tags with React.createElement('div', ...). Because the output code explicitly references the React object, the React object must be in scope. That's why you're forced to import it in every single file, even if you aren't using any other React hooks or methods in that specific file.
// tsconfig.json { "compilerOptions": { "jsx": "react" } } // UserDashboard.tsx import React from 'react'; // Mandatory, otherwise: "React refers to a UMD global" export const UserDashboard = () => { return <div>Welcome Back!</div>; };It feels like boilerplate, and it is. Beyond the annoyance of typing it, it also creates a slight overhead and makes your imports look cluttered. When I'm reviewing code, seeing twenty files with the exact same unused import is just noise.
Letting the compiler do the heavy lifting
The better way—and the way you should be doing it for any modern project—is using
"jsx": "react-jsx". This tells TypeScript to use the "new JSX transform" introduced in React 17. Instead of transforming your tags intoReact.createElement, it transforms them into imports from a special internal package (react/jsx-runtime).The magic here is that the compiler handles the import for you. You can delete
import React from 'react';from your files entirely, and the code will still run perfectly. I usually spend ten minutes cleaning up a legacy codebase just by changing this one flag and running a global find-and-replace to remove the redundant imports.// tsconfig.json { "compilerOptions": { "jsx": "react-jsx" } } // UserDashboard.tsx // No React import needed here! export const UserDashboard = () => { return <div>Welcome Back!</div>; };When you aren't actually using React
Now, here is where things get interesting. What if you're using Preact, or SolidJS, or some other library that uses JSX but isn't React? If you just use
react-jsx, your code will try to import fromreact/jsx-runtimeand crash because you don't have React installed.You might be tempted to go back to the old
"jsx": "react"and manually import the factory function, but that's a step backward. Instead, you usejsxImportSource. This allows you to tell TypeScript: "Use the new transform logic, but instead of looking in the 'react' package, look in the 'preact' package." It's the most flexible way to handle JSX because it decouples the syntax from the specific library implementation. I prefer this approach because it keeps the source code clean while giving you total control over the underlying engine.// tsconfig.json { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" } }
📋 Practical Task
Migrating a Legacy UserProfile Component to the Modern JSX Transform
You have been handed a legacy project with a UserProfile.tsx component and a tsconfig.json file. The project is currently using the old JSX transform, forcing developers to manually import React in every file.
Your task:
- Modify the
tsconfig.jsonto use the modernreact-jsxtransform. - Remove the unnecessary
import React from 'react';line from theUserProfile.tsxfile without breaking the component.
Starting Code:
// tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"jsx": "react",
"strict": true
}
}
// UserProfile.tsx
import React from 'react';
interface Props {
username: string;
}
export const UserProfile = ({ username }: Props) => {
return <div>User: {username}</div>;
};
There are no comments for now.