템플릿 및 JS API를 위한 인라인 스크립트 태그 현대화

테마에서 <script type='text/discourse-plugin> 또는 <script type='text/x-handlebars'> 사용은 이제 비추천(deprecated)됩니다. 테마에서 이러한 태그를 사용하는 경우 아래 지침에 따라 업데이트해야 합니다.

일반적인 <script><script type='text/javascript'>는 이 변경 사항의 영향을 받지 않습니다.

타임라인

이는 대략적인 추정치이며 변경될 수 있습니다

  • 2025년 5월 - 콘솔 비추천 메시지 활성화

  • 2025년 7월 - 관리자 경고 배너 활성화

  • 2025년 9월 말 2026년 3월 - 기능 제거

<script type='text/x-handlebars'> 변환

이 방법을 통해 도입된 템플릿은 전용 .hbs 파일로 이동하거나 gjs 파일로 리팩토링해야 합니다.

HBS로 유지하려면, 커넥터 템플릿은 다음 위치에 배치할 수 있습니다:

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

그리고 컴포넌트 템플릿은 다음 위치에 배치할 수 있습니다:

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

:warning: 2026년 3월 이후 .hbs 파일도 비추천 대상이 됩니다. 이 변환을 완료한 후에는 Deprecating .hbs file extension in themes and plugins 의 지침으로 진행하세요.

최신 .gjs 형식으로 커넥터와 컴포넌트를 빌드하려면 테마 개발자 튜토리얼의 해당 장을 참고하세요:

<script type='text/discourse-plugin'> 변환

이 태그 내부의 코드는 전용 JavaScript 파일로 마이그레이션할 수 있습니다.

관리자 패널 인터페이스를 통해 테마를 개발하는 경우, <script>에서 코드를 복사하여 JS 탭(// your code here이라고 표시된 곳)으로 이동하세요.

로컬에서 테마를 개발하는 경우, 다음 위치에 새 파일을 생성하세요.

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

그리고 아래 래퍼를 추가하고 표시된 위치에 코드를 배치하세요:

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

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

스크립트 태그에서 다른 JS 모듈을 가져오는 유일한 방법은 require() 구문을 사용하는 것이었습니다. .js 파일에서는 여전히 작동하지만 곧 비추천 대상이 되므로, 이제 현대적인 ES6 import로 변환하는 것이 좋습니다. 예를 들어:

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

JS 초기화자에 대한 자세한 정보는 다음을 참조하세요:

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.

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!

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.

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

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>);
});

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.

That did the trick perfectly, thank you!

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"; } 
    });
  }
});

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:)

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

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: