How to Build a Mendix Video Player Widget with TypeScript | Mendix

Skip to main content

How to Build a Mendix Video Player Widget with TypeScript

Key Takeaways

  • Learn to create a Mendix widget from scratch in TypeScript.
  • Define widget properties in XML and configure them in Studio Pro.
  • Implement a video player using the native HTML <video> element
  • Set up and test a REST API in Mendix to serve video content

It’s halfway through 2025, and I have yet to make a widget. Add in that Mendix 11 is officially released and suddenly I feel out of practice. It’s always a good idea to keep up to date with your favorite technologies, so I decided to build something to test myself and keep my skills sharp.

 The challenge

Build a simple video player widget for a web app, without any repositories from GitHub or any other code sharing platforms. In addition, I will be working in TypeScript.

1. Preparation

My first steps were to install and update all the tools I would need. If you’re following along, make sure to download and install:

Finally, I worked through the pluggable widget tutorial in Mendix Docs, to brush up on the basics.

2. Getting started

To begin development, I created a new app in Studio Pro.

  • Create a new app in Studio Pro using the Blank Web App template.
  • Navigate to the project directory.
  • Create a new folder named MyWidget
  • Open a terminal and navigate to the folder

3. Scaffolding the widget

Generating the widget scaffold is simple using the Yeoman Widget Generator. To get started, in the terminal I had open already, I started the generator using this command:

npx @mendix/generator-widget MyVideoPlayer

The generator then guided me through the remaining setup with a few simple questions:


Widget name: MyVideoPlayer
Widget Description: My widget description
Organization Name: Mendix
Copyright: {Your copyright date}
License: {Your license}
Initial Version: {Your initial version number}
Author: {Your author name}
Mendix App path: ../../
Programming language: TypeScript
Which type of components do you want to use? Function Components
Widget type: For web and hybrid mobile apps
Widget template: Empty widget (recommended for more experienced developers)
Unit tests: No
End-to-end tests: No

After answering all the questions, the Widget Generator created the required files in my app directory. I then opened the MyVideoPlayer file in Visual Studio Code to start coding the widget.

4. Defining the widgets properties

Now that the widget skeleton exists, we can start adding to it. I updated the widget properties using xml in the MyVideoPlayer.xml file.

<?xml version="1.0" encoding="utf-8"?>
<widget id="mendix.myvideoplayer.MyVideoPlayer" pluginWidget="true" needsEntityContext="true" offlineCapable="true" supportedPlatform="Web"
    xmlns="http://www.mendix.com/widget/1.0/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../node_modules/mendix/custom_widget.xsd">
    <name>My Video Player</name>
    <description>My widget description</description>
    <icon/>
    <properties>
        <propertyGroup caption="General">
            <property key="videoUrl" type="attribute" required="true">
                <caption>Video URL</caption>
                <description>Direct URL to the video file (e.g. CDN/API)</description>
                <attributeTypes>
                        <attributeType name="String"/>
                    </attributeTypes>
            </property>
            <property key="controls" type="boolean" defaultValue="true">
                <caption>Show controls</caption>
                <description>Toggles controls on or off</description>
            </property>
            <property key="autoPlay" type="boolean" defaultValue="true">
                <caption>Autoplay</caption>
                <description>Autoplay on video load</description>
            </property>
            <property key="muted" type="boolean" defaultValue="true">
                <caption>Muted</caption>
                <description>Include sound with your video</description>
            </property>
            <property key="loop" type="boolean" defaultValue="true">
                <caption>Loop</caption>
                <description>Enable back to back playback</description>
            </property>
            <property key="width" type="integer" defaultValue="640">
                <caption>Width (px)</caption>
                <description>Width of the video player in pixels</description>
            </property>
            <property key="height" type="integer" defaultValue="360">
                <caption>Height (px)</caption>
                <description>Height of the video player in pixels</description>
            </property>
        </propertyGroup>
    </properties>
</widget>

This  creates several properties used to configure the widget.
  • The video URL (String)
  • Show controls (Boolean)
  • Autoplay (Boolean)
  • Muted (Boolean)
  • Loop (Boolean)
  • Width (Integer)
  • Height (Integer)

These are just some basic options to configure the widget’s properties in Studio Pro, the URL is passed via the string attribute VideoURL, which will have to be set outside of the widget and passed in via the context entity.

5. Implementing the widget

With the properties taken care of, I moved on to creating the widget itself. I opened MyVideoPlayer.tsx in Visual Studio Code. As part of the challenge I set, I couldn’t use any video libraries to make the widget. I decided to just use <HTMLVideoElement> a to play the video. The code below is how I create the video player, pass in the properties, and display the video on the page.

import {createElement, useRef} from "react";

import "./ui/MyVideoPlayer.css";

// The generator creates this file for you after editing the XML:
import type { MyVideoPlayerContainerProps } from "../typings/MyVideoPlayerProps";

export default function MyVideoPlayer(props: MyVideoPlayerContainerProps) {
  const videoUrl = props.videoUrl?.value ?? "";//assing video url to entity attribute in Studio Pro
  const {
    controls,
    autoPlay,
    muted,
    loop,
    width,
    height,
    class: className,
    style,
    tabIndex
  } = props;

  const videoRef = useRef(null);

  if (!videoUrl || videoUrl.length === 0) {//URL String validation
    return (
//Video Player error message No video URL provided
); } return (//return video player ); }

6. A nice to have

A final step before finalizing the widget is to update the MyVideoPlayer.editorPreview.tsx file. This is what Studio Pro uses at design time to show in the editor window (how the widget looks in Studio Pro, not in the browser).
This is just returning a div with some simple styles attached to it, so that the widget looks nice design view.

import { createElement } from "react";

export function preview() {
  return (
    <div
      style={{
        width: 320,
        height: 180,
        border: "1px dashed #bbb",
        borderRadius: 12,
        display: "grid",
        placeItems: "center",
        fontSize: 12,
        color: "#666"
      }}
    >
      Video Player (bind Video URL)
    </div>
  );
}

export function getPreviewCss() {
  return "";
}

7. Adding the widget to Studio Pro

Now that the work was done, I compiled the widget using the build command in the terminal.

npm run build //(You can also use npm start to have the widget autocompile after every change)

This command compiles the code into a package and copies it into the widget folder for the app.

8. In Studio Pro

It’s important to synchronize the app with the project directory and update all widgets after successfully building the widget, or else it may not show up in the toolbox.

  • I created a persistable entity in the domain model called Video. The entity is a generalization of FileDocument and has three attributes: Title (String), VideoID (Autonumber), and VideoURL(String).
  • The widget expects a URL to play the video. To accomplish this, I decided to expose the videos as a REST API, and to use the VideoID(AutoNumber) as the Key in the Get_ByKey endpoint. The URL for the REST API can function as a URL for the video player.

I opted to generate the REST API from the domain model, by right clicking the Video entity and choosing to expose it as a REST API. I let Studio Pro create the API and used the AutoNumber field as the Key for the service (I created the GetAll and GetByKey endpoints for the API, but only the GetByKey is needed). I modified the GetByKey endpoint, by updating the microflow which handles the response.

For the service to work correctly, the correct Content Type header must be attached to the response. I added the HTTPHeader with the following values:

  • Key: Content-Type
  • Value: video/mp4

I also set the association to the HTTP Response entity.

9. Configuring the widget

The widget expects an attribute from a context entity, so I placed it in a dataview when adding it to the page.

 

I then opened its properties to configure it.

  • Video URL (String from Context Entity)
  • Show controls (true)
  • Autoplay (true)
  • Muted (false)
  • Loop (false)
  • Width (640)
  • Height (360)

10. Setting the URL

A final step – automatically setting the URL for the video when saving the video file. In the microflow attached to the save button for the video I added a change action and set the string using the Get application URL java action from Community Commons

  • $AppURL+'/rest/myvideoservice/v1/video/'+$Video/VideoID
  • (e.g. http://localhost:8080/rest/myvideoservice/v1/video/2)

11. Testing the widget

After all this, it was finally time to test the widget. I created a simple screen to upload videos to the entity and display the VideoID field and displayed the URL for the GetByKey API endpoint for that video.

After running the app and uploading a test video, i was able to get a complete URL for the video and updated the Video URL property of the widget.
One final rerun of the app, and I was able to see my widget load correctly and play my video correctly! Success at last!

Final thoughts

This was a fun challenge, as I haven’t worked with multimedia in many projects. In the future, I would like to extend the widget in several key areas:

    1. More refined styling
    2. Optional features (2x speed, captions, etc)
    3. Add in “posters” for the video (Thumbnails, images as placeholders)

I really enjoyed testing myself with this fun challenge, and I hope you enjoyed reading it. If you have any suggestions, critiques or comments – let me know and I might write an update in the future.

Choose your language