How to Import JSON in TypeScript Without Losing Your Mind
\n\nIf you've ever tried to do import data from './config.json' in TypeScript, you know that feeling: the compiler turns red, complains, and throws an error Cannot find module. Sound familiar? You bet! The question about importing JSON in TypeScript is one of the most popular on Stack Overflow. And it's no wonder: JSON is used everywhere — configs, localization, mock data, static lists. But TypeScript doesn't know how to work with JSON files by default.
In this tutorial, we'll cover all the ways to import JSON in TypeScript — from simply enabling a flag in tsconfig to advanced typing. You'll learn how to make TypeScript play nice with JSON, and you'll never see that red error again. Let's go!
\n\nWhy Doesn't TypeScript See JSON?
\nBefore diving into the code, let's understand the reason. TypeScript is a superset of JavaScript that adds static typing. By default, the TypeScript compiler expects all imported modules to be .ts or .js files. JSON is not a module; it's a data format. TypeScript simply doesn't know how to interpret it.
\nThe solution lies in two areas:
\n- \n
- Compiler configuration — enable JSON support via the
resolveJsonModuleflag. \n - Module declaration — manually describe the type for JSON files if the flag isn't suitable. \n
Method 1: Enable resolveJsonModule (The Right Way)
\nThe simplest and recommended way is to add the resolveJsonModule option to tsconfig.json. This flag tells TypeScript: "Hey buddy, let's import JSON like regular modules."
// tsconfig.json\n{\n "compilerOptions": {\n "resolveJsonModule": true,\n "esModuleInterop": true\n }\n}\nAfter that, you can safely write:
\n// config.ts\nimport config from './config.json';\n\nconsole.log(config.apiUrl); // works!\nconsole.log(config.port); // works!\nWhat's happening here? TypeScript automatically creates types for the imported JSON. If your JSON has a field apiUrl: string and port: number, TypeScript will infer their types and check their usage. Cool, right?
But there's a catch: resolveJsonModule also requires enabling esModuleInterop or allowSyntheticDefaultImports for the default import to work correctly. Without this, you'll get an error when trying to import.
Method 2: Import with Named Exports
\nSometimes you don't need the entire JSON, only specific fields. TypeScript supports this too:
\nimport { apiUrl, port } from './config.json';\n\nconsole.log(apiUrl); // string\nconsole.log(port); // number\nThis is possible thanks to so-called "tree-shaking" in bundlers (Webpack, Vite), but at the TypeScript level, it's just object destructuring. However, remember: if you use resolveJsonModule, TypeScript automatically creates types for all fields, so you can safely import either the entire object or its parts.
Method 3: require Instead of import (For Older Projects)
\nIf you're working on a project where resolveJsonModule cannot be enabled for some reason (e.g., conflict with other settings), you can use the good old require:
const config = require('./config.json');\n\nconsole.log(config.apiUrl); // works, but without types\nThe problem with this approach is obvious: you lose typing. TypeScript doesn't know what's inside config and will treat it as type any. This opens the door to errors. If you must use require, at least add a type declaration:
interface Config {\n apiUrl: string;\n port: number;