Modernizing inline script tags for templates & JS API

Using <script type='text/discourse-plugin> or <script type='text/x-handlebars'> in themes is now deprecated. Any use of these tags in themes should be updated according to the instructions below.

Regular <script> and <script type='text/javascript'> are unaffected by this change.

Timeline

These are rough estimates, subject to change

  • May 2025 - console deprecation messages enabled

  • July 2025 - admin warning banners enabled

  • September 2025 - removal of feature

Converting <script type='text/x-handlebars'>

Templates introduced using this method should be moved to dedicated .hbs files, or refactored into gjs files.

To keep as HBS, connector templates can be placed in:

{theme}/javascripts/discourse/connectors/{outlet-name}/{connector-name}.hbs

and component templates can be placed in:

{theme}/javascripts/discourse/components/{component-name}.hbs

To build connectors and components in the modern .gjs format, check out this chapter of the theme developer tutorial:

Converting <script type='text/discourse-plugin'>

Code inside these tags can be migrated to a dedicated JavaScript file.

If you develop your theme via the admin panel interface, copy the code out of the <script>, and move it into the JS tab (where it says // your code here).

If you develop your theme locally, create a new file at

{theme}/javascripts/discourse/api-initializers/init-theme.js

then add this wrapper, and place your code in the indicated spot:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  // Your code here
});

In script tags, the only way to import other JS modules was using the require() syntax. While that will still work in a .js file, it will soon be deprecated, so now would be a good time to convert it to modern ES6 imports. For example:

- const I18n = require("discourse-i18n").default;
+ import I18n from "discourse-i18n";
- const { h } = require("virtual-dom");
+ import { h } from "virtual-dom";

For more information on JS initializers:

17 Likes

Maybe a very dumb question, but I have a very very simple theme component that I put directly into the admin console under <head>:

<script type="text/x-handlebars" data-template-name="/connectors/top-notices/whos-online-below-site-header">
{{whos-online}}
</script>

If I’m following this post correctly, does it mean that I now have to create a separate theme component folder, host it on GitHub and add the component just for something as simple as adding to a plugin outlet?

I sure hope not, as it’ll break most of my simple theme components :grimacing:

There is the JS tab now, so you can probably use api.renderInOutlet now.

1 Like

Ah, I didn’t see that this was possible to do with connectors/components as well, but in the linked article found this:

Maybe easier than I thought, thank you!

2 Likes

Thank you for this post!
May be a very basic question I believe:
Since we do not have a sandbox, I need to be sure before I proceed.
I have to update a theme affected so am I correct if say that all the code related to ā€œScriptā€ from ā€œheadā€ tab need to be moved into the JS tab as illustrated below:

Almost, but not quite. You’d want to remove the script tags, and change the imports to something like:

import { ajax } from "discourse/lib/ajax";

Then, paste all this in the JS tab, inside:

Our Discourse site (hosted by Discourse) is currently generating the following error bar (which is what led me to this thread):

I’m not aware that we’ve done any cutomization of the ā€˜Light’ theme of our own, and looking at its admin page, am not seeing anything suggesting this is anything more than the system-provided default (but could easily be missing something).

For a hosted site like ours, is this likely to self-resolve over time, or does it require some action on our part?

Thanks,
-Brad

You will need to take action to resolve this - it won’t go away on its own.

I took a quick look at your site in chrome dev tools, and it looks like the relevent code is related to adding the ā€˜chapel’ language to highlightjs.

If you visit your ā€˜Light’ theme, and hit ā€˜edit code’, you should be able to find this under one of the HTML tabs. Then you can follow the instructions in the OP of this topic to move it to the ā€˜JS’ tab.

1 Like

Thanks @david! I’d forgotten that we’d done some customizations to get Chapel highlighting and am not sure I would’ve gotten there on my own from the error banner, so appreciate the assistance and pointers.

-Brad

1 Like

I have a simple .html file:

<script type='text/x-handlebars' data-template-name='/connectors/below-site-header/oprs-top-container'>
    <div id='WW_T_D_1' class='oprs-top-leaderboard'></div>
</script>

I’m kind of confused with different ways to do this migration. Appreciate if someone explains what to do. Thanks

Just renderInOutlet to the connector

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  api.renderInOutlet("below-site-header", <template>
    <div id='WW_T_D_1' class='oprs-top-leaderboard'></div>
  </template>);
});
2 Likes

Thank you. I tried and it didn’t work :confused:

This my structure and these are

below-site-header.js


export function test() {
    let test2 = document.querySelector('.test');
    console.log('test ', test2);
}

And below-siteheader-connectors.hbs

<div class="test"></div>

I’m new to this so any help is appreciated. I took this repo from a previous dev.

Hi there, could you share the link to your Github repo? Thanks!

So I’ve slapped my tiny ā€œmake-avatar-biggerā€ script into the JS tab (out of Desktop - <head> ), and that works simply enough, but is there at least a quick ā€˜n dirty way to keep it only applying in Desktop? My mobile avatars are also now large and it all looks rather silly.

The code for ref is:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
 api.changeWidgetSetting('post-avatar', 'size', '70');
});

Hmm… maybe adjust this with CSS?

Or, maybe:

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  const site = api.container.lookup("service:site");
  if (!site.mobileView) {
    api.changeWidgetSetting('post-avatar', 'size', '70');
  }
});

Something from the top of my head.

1 Like

That did the trick perfectly, thank you!

1 Like

Just a friendly reminder: changeWidgetSetting on post-avatar is a deprecated API and will be removed soon.

Maybe you should change your code to this

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  const site = api.container.lookup("service:site");
  if (!site.mobileView) {
    api.modifyClass("component:post/avatar", (SuperClass) => class extends SuperClass {
      get size() { return "70"; } 
    });
  }
});
4 Likes

I think there’s also an official theme component for this as well? Avatar Size and Shape

Not sure if it fits the particular use case, but it seems like it should (with the added bonus that someone will fix it if it needs updating :slight_smile:)

6 Likes

I have upgraded all the way from quick 'n dirty to officially supported, thanks all!

4 Likes

Noting for people with legacy components (using separate Desktop/Mobile handling) that the JS tab is only available in the Common section - hopefully saves somebody else a few searches :slight_smile:

1 Like