How to Fix "SyntaxError: Unexpected token '<'" Jest Error?

Why Does This Happen?

When running Jest tests with tsx (or jsx) React components, you might encounter the "SyntaxError: Unexpected token '<'" error like the following:

Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /path/to/__tests__/MyComponent.test.tsx:27
            (0, react_2.render)(<client_1.MyComponent foo="bar">
                                ^

    SyntaxError: Unexpected token '<'

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1505:14)

This error indicates that Jest is unable to parse the file due to non-standard JavaScript syntax or inadequate configuration. The "'<'" in the error message suggests that Jest is not able to transpile code in a tsx (or jsx) file because it might not be configured to do so.

How to Fix the Issue?

To fix this issue you need to make sure Jest is configured to transpile the code in a tsx (or jsx) file. You can do so by checking for the following configurations:

  1. You're Using the Right Transformer;
  2. You're Using the Right Jest Testing Environment;
  3. You're Using the Right "jsx" Mode in tsconfig.json.

Choosing the Right Transformer

You can use babel-jest or ts-jest as a transformer:

Using babel-jest as the Transformer

You should use babel-jest as a transformer in the following cases:

  • When you have jsx files, or;
  • When you have tsx files, but do not want to type-check your tests as they are run.

To set it up, you need to first install the following packages:

npm install --save-dev babel-jest @babel/core @babel/preset-typescript @babel/preset-env

After that, you must create a babel.config.js file (or .babelrc) with a minimum configuration like the following:

module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }],
    '@babel/preset-typescript',
  ],
};

@babel/preset-typescript is only required for transpiling TypeScript code (tsx files). For jsx files, you can exclude it.

Based on the Babel configuration, Jest will automatically transpile tsx (or jsx) files. However, if you wish, you can explicitly define babel-jest as a transformer for these files in your jest.config.js:

// jest.config.js
module.exports = {
  // ...
  testEnvironment: 'jsdom',
  transform: {
    '^.+\\.tsx?$': 'babel-jest',
  },
  // ...
}

Using ts-jest as the Transformer

You should use ts-jest as a transformer when you have tsx files and want to type-check your tests as they are run.

Although, by default ts-jest performs type-checking, it is possible to disable it by passing isolatedModules: true as an option.

To set it up, you need to first install the following packages:

npm install --save-dev ts-jest @types/jest

After that, you need to add the following settings to your jest.config.js file:

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
};

If this setting does not work for you, as an alternative, you can try explicitly setting ts-jest as the transformer for tsx files:

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
};

Ensuring the Right Jest Test Environment

By default, jest uses the "node" as the test environment, making tests meant for a browser environment invalid. Therefore, you must set testEnvironment to jsdom in your jest.config.js file, like so:

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  // ...
};

This ensures a browser-like environment for tests that are meant for a browser environment.

Make sure you have the jest-environment-jsdom package installed for this to work.

Ensuring the Right "jsx" Mode in tsconfig.json

In your tsconfig.json file, you must ensure that you're using the right value for the "compilerOptions" "jsx" property, i.e. one of the following:

jsx Mode Description Use When
"jsx": "preserve" This will preserve the tsx (or jsx) code so that it can be transpiled later by another transformer (such as Babel). The output will have a .jsx extension. Use this when you're using babel-jest to transpile your tsx (or jsx) files.
"jsx": "react-native" Same as the "preserve" option, but the output will have a .js extension.
"jsx": "react" This will emit React.createElement() and serve .js files. It does not need to go through any further transpiling later. Use this when you're using ts-jest to transpile your tsx (or jsx) files.
"jsx": "react-jsx" Same as the "react" option, but relevant for React 17+. It helps you avoid the necessity of importing React in every jsx file.
"jsx": "react-jsxdev" Same as the "react-jsx" option, but uses "react/jsx-dev-runtime" in the transformed code (instead of "react/jsx-runtime") to add debugging information.

You must specify one of these "jsx" options in the "compilerOptions" in your tsconfig file, like so:

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react",
    // ...

This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.