Creating Your First Component
Building an Instrument Template
In MSFS, all HTML instruments require a HTML template to render to, as well as to pull in any other Javascript and CSS that go along with the instrument. Create a file MyInstrument.html
in your project root with the following content:
<script type="text/html" id="MyInstrument">
<div id="InstrumentContent"></div>
</script>
<link rel="stylesheet" href="MyInstrument.css" />
<script type="text/html" import-script="/Pages/VCockpit/Instruments/MyInstrument/MyInstrument.js"></script>
This is a very basic template that can be loaded by the MSFS VCockpit system. First we defined a HTML template, where we put just one div
element with the ID InstrumentContent
. We then name this HTML template with the ID MyInstrument
. This ID will be used later to inform the VCockpit system which HTML template to use to display the instrument.
We then link to our bundled CSS stylesheet, which we will place alongside our HTML. Finally, we link what will become our bundled and compiled Javascript using the VCockpit import-script
attribute. This lets the VCockpit system bootstrap its own dependencies, and then load any further dependencies it finds with import-script
attributes.
Linking Your Instrument to Code
In the MSFS VCockpit system, all instruments must originate and extend from the class BaseInstrument
. This class contains code that the system communicates with to bootstrap and initialize the instrument, and place it into the HTML that is displayed on the screen in the simulator. It is then possible to register that class with the VCockpit system as the instrument to be displayed.
Create a file MyInstrument.tsx
in the project root alongside your MyInstrument.html
file. Add the following code to the file:
/// <reference types="@microsoft/msfs-types/Pages/VCockpit/Core/VCockpit" />
class MyInstrument extends BaseInstrument {
get templateID(): string {
return 'MyInstrument';
}
}
registerInstrument('my-instrument', MyInstrument);
Note that this class contains the property templateID
, which returns a string. This property must be provided and the returned string must match the name of your instrument template you provided in the HTML, in this case MyInstrument
.
Finally, we call the VCockpit function registerInstrument()
to register our instrument class with the system. This function takes two parameters: the name that you would like to give your instrument (any name is fine, and need not match any other IDs or tags), and the instrument class for the VCockpit system to instantiate.
registerInstrument
is a method that comes from the underlying MSFS JS SDK, and is not part of the avionics framework. As such it needs to be referenced from the msfs-types
module, which is what the first line of the sample code does. You will need to include a similar reference for any other methods that come from the underlying SDK.
Building a Hello World Component
We are going to create a very simple component that displays the text Hello World!
on the page, within the instrument. Create a file MyComponent.tsx
in the project root, and add the following code:
import { FSComponent, DisplayComponent, VNode } from '@microsoft/msfs-sdk';
export class MyComponent extends DisplayComponent<any> {
public render(): VNode {
return (
<div class='my-component'>Hello World!</div>
);
}
}
Let's go through what's happening in this code a bit.
Framework Imports
import { FSComponent, DisplayComponent, VNode } from '@microsoft/msfs-sdk';
In order to use code from the framework, it must be imported into the TypeScript file. While a complete discussion of the usage of imports is beyond the scope of these documents, do note that in order to use JSX within a component, you must at least import FSComponent
. This is similar to needing to import react
when using React components.
DisplayComponent
export class MyComponent extends DisplayComponent<any> {
...
In the FSComponent framework, all components must extend from DisplayComponent
, much like in React, all class components must extend from React.Component
. All components must implement at least one public method, named render()
, that returns some JSX elements. When the component is rendered to the DOM, this is what will be placed there. This can contain other HTML elements, like div
s, other components, or any combination of the two.
In this way, just as in React, one can compose components and HTML together seamlessly.
Rendering Your Component
Right now, our component lives on its own, ready to display its contents, but it has not been rendered anywhere into our instrument. Go back to the MyInstrument.tsx
file and add the following to the top of the file to import our component as well as enable the usage of JSX:
import { FSComponent } from '@microsoft/msfs-sdk';
import { MyComponent } from './MyComponent';
Then, add this implementation of connectedCallback()
to the MyInstrument
class:
public connectedCallback(): void {
super.connectedCallback();
FSComponent.render(<MyComponent />, document.getElementById('InstrumentContent'));
}
Always call super.connectedCallback()
first thing in any implementation of BaseInstrument.connectedCallback()
. The base class does important work that is necessary for your instrument to function inside its implementation. Failing to call super
will cause your instrument to fail during load.
The full contents of your file should now look like:
/// <reference types="@microsoft/msfs-types/Pages/VCockpit/Core/VCockpit" />
import { FSComponent } from '@microsoft/msfs-sdk';
import { MyComponent } from './MyComponent';
class MyInstrument extends BaseInstrument {
get templateID(): string {
return 'MyInstrument';
}
public connectedCallback(): void {
super.connectedCallback();
FSComponent.render(<MyComponent />, document.getElementById('InstrumentContent'));
}
}
registerInstrument('my-instrument', MyInstrument);
FSComponent.render()
You may notice that we added a call to FSComponent.render()
in our instrument's connectedCallback()
function. Like React.render()
, this function takes JSX and renders it to the specified element. In this case, we are taking our newly created MyComponent
component, and rendering it to the element with the ID InstrumentContent
. This was the ID that we gave our single div
element inside our instrument template.
The method connectedCallback()
will be called by the browser DOM when the MyInstrument
element, registered via registerInstrument()
, is instantiated fully and inserted into the DOM on the page. Therefore, our component will be rendered using FSComponent.render()
once the instrument is in the DOM, and guarantees that the div with the ID InstrumentContent
is available to render to.
Build Your Component
You can now build your component using the NPM script that we added in the previous steps:
> npm run build
This will run our Rollup build and output our built files to the build
folder. You can then copy the HTML file from the root and the compiled Javascript file from build
to your MSFS package sources folder, and run your package build and resync in MSFS to see your instrument on your panel.
The location of the Javascript file within the package sources must match the path that was provided in the HTML template import-script
attribute. In this case, that attribute is set to /Pages/VCockpit/Instruments/MyInstrument/MyInstrument.js
. All HTML sources loaded by the Coherent GT framework in MSFS reside in the html_ui
root folder, so the final package folder path that you should copy the file to should be html_ui/Pages/VCockpit/Instruments/MyInstrument/MyInstrument.js
.