ForgeUI UI Kit 2 with Typescript: Runtime error i.default.createElement is not a function

I’m using one of the Admin Page Console Apps for JIra as well as Forge UI Kit 2. This works out of the box, however I have run into a runtime issue after adding support for Typescript.

I’ve made the following changes to the templated code:
1. renamed the frontend/src/index.jsx file to index.tsx
2.added the following tsconfig.json file

{
  "compileOnSave": true,
  "compilerOptions": {
    "outDir": "./dist/",
    "module": "commonjs",
    "target": "es2017",
    "sourceMap": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "lib": ["dom", "es2017"],
    "types": ["node", "react"],
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "jsx": "react",
    "jsxFactory": "ForgeUI.createElement",
    "jsxFragmentFactory": "Fragment",
    "typeRoots": [
      "./node_modules/@types"
    ],
    "baseUrl": "."
  },
  "include": [ "./**/*" ],
  "exclude": [ "node_modules", "build", "dist" ]
}
  1. Modified the manifest.yml file:
app:
  id: 
modules:
  jira:adminPage:
    - key: stpa-app-hello-world-admin-page
      resource: main
      resolver:
        function: resolver
      render: native
      title: stpa-app
  function:
    - key: resolver
      handler: index.handler
resources:
  - key: main
    path: src/frontend/index.tsx
  - key: static-icons
    path: static/icons
  - key: static-images
    path: static/images
permissions:
  external:
    images:
      - stpa.com
  scopes: []

Everything compiles with: tsc -d -p .
forge deploy and forge install complete ok

However at runtime, I’m seeing the following error

Uncaught TypeError: i.default.createElement is not a function
    at 3181 (index.tsx:348:5)
    at a (bootstrap:24:23)
    at startup:6:27
    at startup:6:47

The index.tsx code is as follows

import React,  { useEffect, useState } from 'react';
import ForgeReconciler, { Text } from '@forge/react';
import ForgeUI from '@forge/react';
import Fragment from '@forge/react';
import { invoke } from '@forge/bridge';

const App = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    invoke('getText', { example: 'my-invoke-variable' }).then(setData);
  }, []);
  return (
    <>
      <Text>Hello world!</Text>
      <Text>{data ? data : 'Loading...'}</Text>
    </>
  );
};
ForgeReconciler.render(
 <React.StrictMode>
    <App />
 </React.StrictMode> 
);

Any help would be much appreciated.

Thanks!

Welcome to the Atlassian developer community @MarkLombardi,

According to the documentation, the resources path should be a directory, not a file. So your resources section like this:

resources:
  - key: main
    path: src/frontend/index.tsx

Should be like this:

resources:
  - key: main
    path: src/frontend

I am actually a little surprised the lint/deploy didn’t complain. And, I’m not 100% that is your only problem, or the one causing the error. I struggled with TypeScript + UI Kit 2 and haven’t been able to create a successful project yet. As such, I don’t have a working example to try or point to.

Thanks for responding @ibuchanan its actually the other way around. You need to specific the path to a file, not a folder. So thats not an issue.

I think the core of the issue is, the tsc compiler should be creating code like the following:

Compiled from index.jsx

const App = () => {
    const [data, setData] = (0, react_1.useState)(null);
    (0, react_1.useEffect)(() => {
        (0, bridge_1.invoke)('getText', { example: 'my-invoke-variable' }).then(setData);
    }, []);
    return (ForgeUI.createElement(react_1.default.Fragment, null,
        ForgeUI.createElement(react_2.Text, null, "Hello world!"),
        ForgeUI.createElement(react_2.Text, null, data ? data : 'Loading...')));
};

Instead it’s generating the following from index.tsx:

const App = () => {
    const [data, setData] = (0, react_1.useState)(null);
    (0, react_1.useEffect)(() => {
        (0, bridge_1.invoke)('getText', { example: 'my-invoke-variable' }).then(setData);
    }, []);
    return (react_3.default.createElement(react_4.default, null,
        react_3.default.createElement(react_2.Text, null, "Hello world!"),
        react_3.default.createElement(react_2.Text, null, data ? data : 'Loading...')));
};

What fixed this problem for me was making the following changes to tsconfig.json

{
  "compileOnSave": true,
  "compilerOptions": {
    "outDir": "./dist/",
    "module": "commonjs",
    "target": "es2017",
    "sourceMap": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "lib": ["dom", "es2017"],
    "types": ["node", "react"],
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "jsx": "react-jsx",
    // "jsxFactory": "ForgeUI.createElement",
    // "jsxFragmentFactory": "Fragment",
    "typeRoots": [
      "./node_modules/@types"
    ],
    "baseUrl": "."
  },
  "include": [ "./**/*" ],
  "exclude": [ "node_modules", "build", "dist" ]
}
4 Likes