DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

Understanding `'PropsWithChildren'` and `verbatimModuleSyntax` in React + TypeScript 5

 raw `'PropsWithChildren' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.ts(1484)` endraw

'PropsWithChildren' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.ts(1484)

If you’ve recently upgraded to TypeScript 5+ and started seeing this new error in your React project:

'PropsWithChildren' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.ts(1484)
Enter fullscreen mode Exit fullscreen mode

Don’t worry — you didn’t break React.

You just met one of the most important compiler upgrades in recent TypeScript history: verbatimModuleSyntax.


The Problem Example

Let’s say you’re using TanStack Query v5 and write a helper like this:

import { PropsWithChildren } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

export function withQueryClient(ui: React.ReactElement) {
  const client = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  const Wrapper = ({ children }: PropsWithChildren) => (
    <QueryClientProvider client={client}>{children}</QueryClientProvider>
  );

  return { ui, Wrapper };
}
Enter fullscreen mode Exit fullscreen mode

TypeScript 5.0+ throws the error because you’re importing a type (PropsWithChildren) as if it were a runtime value — and you have "verbatimModuleSyntax": true in your tsconfig.json.


Why This Happens

Historically, TypeScript performed import elision — it analyzed which imports were only used for types, and dropped them automatically during compilation.

But this caused headaches when mixing type imports, side-effect imports, and export elision.

So TypeScript 5 introduced a simpler and more explicit rule set:

✅ Under verbatimModuleSyntax, TypeScript no longer elides imports automatically.

You must explicitly mark type-only imports using the type modifier.

Example:

// Old way (TS < 5.0)
import { PropsWithChildren } from 'react';

// New way (TS 5+ with verbatimModuleSyntax)
import type { PropsWithChildren } from 'react';
Enter fullscreen mode Exit fullscreen mode

Now, what you see in your import statements is exactly what you’ll get in the compiled JavaScript.


Fixing the Error (3 Proven Options)

✅ Option 1 — Use a Type-Only Import (Recommended)

import type { PropsWithChildren } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

export function withQueryClient(ui: React.ReactElement) {
  const client = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  const Wrapper = ({ children }: PropsWithChildren) => (
    <QueryClientProvider client={client}>{children}</QueryClientProvider>
  );

  return { ui, Wrapper };
}
Enter fullscreen mode Exit fullscreen mode

👉 This is the correct, future-proof way. It satisfies the compiler and makes your imports explicit.


✅ Option 2 — Use the React Namespace

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

export function withQueryClient(ui: React.ReactElement) {
  const client = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  const Wrapper = ({ children }: React.PropsWithChildren }) => (
    <QueryClientProvider client={client}>{children}</QueryClientProvider>
  );

  return { ui, Wrapper };
}
Enter fullscreen mode Exit fullscreen mode

React.PropsWithChildren references the global React types from @types/react, so you don’t need to import anything at all.


⚠️ Option 3 — Disable verbatimModuleSyntax (Not Recommended)

// tsconfig.json
{
  "compilerOptions": {
    "verbatimModuleSyntax": false
  }
}
Enter fullscreen mode Exit fullscreen mode

This brings back TypeScript’s older “import elision” behavior — but removes the clarity and consistency gained in 5.0+.

Use this only as a temporary migration step.


Bonus: Improving the Test Wrapper Pattern

If your wrapper is for testing (e.g., Vitest, Jest), you can enhance it a bit:

import type { PropsWithChildren } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

export function withQueryClient(ui: React.ReactElement) {
  const client = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  function Wrapper({ children }: PropsWithChildren) {
    return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
  }

  return { ui, Wrapper, client };
}
Enter fullscreen mode Exit fullscreen mode

Then in your tests:

import { render } from '@testing-library/react';
import { withQueryClient } from './withQueryClient';

test('renders data', () => {
  const { ui, Wrapper } = withQueryClient(<MyComponent />);
  render(ui, { wrapper: Wrapper });
});
Enter fullscreen mode Exit fullscreen mode

Deep Dive — What verbatimModuleSyntax Actually Does

Before TypeScript 5.0, the compiler tried to “guess” which imports were needed at runtime.

This guessing game led to subtle inconsistencies across compilers, bundlers, and file types.

Example (before TypeScript 5.0)

import { Car } from './car';
export function drive(car: Car) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

After transpilation, TypeScript elides the import entirely:

export function drive(car) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

But this behavior was inconsistent — what if ./car had side effects?

What if another compiler (like Babel) didn’t drop it the same way?

The Fix: Simplicity via Explicitness

verbatimModuleSyntax means:

  • Imports without type → stay in emitted JS.
  • Imports with type → erased entirely.

“What you see is what you get.”

That makes builds predictable and aligns TypeScript with the ECMAScript standard.


Related Deprecations

  • --importsNotUsedAsValues → Deprecated.
  • --preserveValueImports → Deprecated.
  • --verbatimModuleSyntax → The new standard for clarity and correctness.

When It Breaks — Common Pitfalls

Scenario Problem Fix
Importing only types as values 'X' is a type and must be imported using a type-only import Use import type { X }
Using both values & types from the same module Compiler warns you Combine both: import { Button, type ButtonProps } from './Button'
Tooling mismatch Old Babel/TS config Ensure your bundler supports modern ESM and TS 5+

Final Takeaway

verbatimModuleSyntax is one of those rare TypeScript changes that simplifies your mental model instead of complicating it.

Old World New World
Implicit import guessing Explicit type/value imports
Complex flags (importsNotUsedAsValues) One clear rule
Risk of side-effect elision Deterministic output

In short:

✅ Use import type for types.

✅ Use import for values.

🧠 Be explicit, not implicit.

With this understanding, the 'PropsWithChildren' error is no longer a mystery — it’s a gentle nudge from TypeScript guiding you toward better module hygiene.


✍️ Written by Cristian Sifuentes — Full‑stack developer & AI/JS enthusiast focused on resilient frontends, scalable architectures, and TypeScript excellence.

Tags: #react #typescript #frontend #architecture #programming

Top comments (0)