# Using the JS API

**URL:** https://meta.discourse.org/t/using-the-js-api/41281
**Category:** Developer Guides
**Tags:** plugin-api, explanation, code
**Created:** [March 18, 2016, 6:52pm UTC](https://meta.discourse.org/t/using-the-js-api/41281 "2016-03-18T18:52:02Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [March 18, 2016, 6:52pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/1 "2016-03-18T18:52:02Z")

</div>

Discourse’s JavaScript API allows themes and plugins to make extensive customizations to the user experience. The simplest way to use it is to create a new theme from the admin panel, click “Edit Code”, and then head to the JS tab.

For file-based themes, the API can be used by creating a file in the `api-initializers` directory. For theme’s that’s `{theme}/javascripts/api-initializers/init-theme.gjs`, and for plugins, it’s `{plugin}/assets/javascripts/discourse/api-initializers/init-plugin.js`. The content should be:

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

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

```

All the available APIs are listed in the [`plugin-api.gjs` source code](https://github.com/discourse/discourse/blob/main/frontend/discourse/app/lib/plugin-api.gjs) in Discourse core, along with a short description and examples.

For a full tuturial, including examples of JS API usage, check out:

> [@Theme Developer Tutorial: 1. Introduction](https://meta.discourse.org/t/theme-developer-tutorial-1-introduction/357796):
>
> This tutorial will teach you how to create a Discourse Theme or Theme Component from the ground up. While this topic assumes no previous experience working on Discourse themes, it does assume some prior experience using [HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML), [CSS](https://developer.mozilla.org/en-US/docs/Learn/CSS) and [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/JavaScript). It’ll also help if you [know your way around GitHub](https://guides.github.com/activities/hello-world/). What are Discourse themes? A theme or theme component is a set of files packaged together designed to either modify Discourse visually or to add new features. Themes In general, themes are not suppo…

* * *

This document is version controlled - suggest changes [on github](https://github.com/discourse/discourse/blob/main/docs/developer-guides/docs/03-code-internals/13-pluginapi.md).

---

<div class="post-metadata">

### Author: ![DeanMarkTaylor](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/deanmarktaylor/32/102462_2.png) [@DeanMarkTaylor](https://meta.discourse.org/u/DeanMarkTaylor)
#### Post date: [March 29, 2016, 3:51am UTC](https://meta.discourse.org/t/using-the-js-api/41281/2 "2016-03-29T03:51:50Z")

</div>

What’s the best way to “import” as a Site Customisation? - is it just to use `require`?

Whilst this works:

```html
<script type="text/discourse-plugin" version="0.1">
var HamburgerMenuComponent = require('discourse/components/hamburger-menu').default;
</script>

```

This does not:

```html
<script type="text/discourse-plugin" version="0.1">
import {default as HamburgerMenuComponent2 } from 'discourse/components/hamburger-menu';

</script>

```

Where I get this error:

```html
<script type="text/discourse-js-error">unknown: 'import' and 'export' may only appear at the top level (3:0)
  1 | Discourse._registerPluginCode('0.1', api => {
  2 |   
> 3 | import {default as HamburgerMenuComponent2 } from 'discourse/components/hamburger-menu';
    | ^
  4 | 
  5 | 
  6 | }); at <eval>:8695:14</script>

```

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [March 30, 2016, 5:05am UTC](https://meta.discourse.org/t/using-the-js-api/41281/3 "2016-03-30T05:05:52Z")

</div>

Instead of importing it, you can look it up with

```js
api.container.lookupFactory('component:hamburger-menu')

```

---

<div class="post-metadata">

### Author: ![Nuck](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/nuck/32/121732_2.png) [@Nuck](https://meta.discourse.org/u/Nuck)
#### Post date: [April 6, 2016, 9:15pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/4 "2016-04-06T21:15:54Z")

</div>

Is there a way to get access to the `h` helper from `virtual-dom` in a site customization? I’m trying to add a simple dropdown widget to use in our header, and I can’t get that darned `h`, even though I can get `createWidget`

---

<div class="post-metadata">

### Author: ![Yuun](https://avatars.discourse-cdn.com/v4/letter/y/977dab/32.png) [@Yuun](https://meta.discourse.org/u/Yuun)
#### Post date: [April 6, 2016, 9:22pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/5 "2016-04-06T21:22:51Z")

</div>

Does:

```plaintext
h = require('virtual-dom').h;

```

not work?

---

<div class="post-metadata">

### Author: ![Nuck](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/nuck/32/121732_2.png) [@Nuck](https://meta.discourse.org/u/Nuck)
#### Post date: [April 6, 2016, 9:25pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/6 "2016-04-06T21:25:02Z")

</div>

> [@Yuun](#):
>
> Does:
> 
> h = require(‘virtual-dom’).h;
> 
> not work?

Yes it does! Works perfectly, thanks a ton!

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [April 6, 2016, 9:25pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/7 "2016-04-06T21:25:55Z")

</div>

I wouldn’t recommend doing it that way as it would likely break future compatibility. You can use this for now but I’ll try to introduce a workaround shortly that will be safer long term.

---

<div class="post-metadata">

### Author: ![Nuck](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/nuck/32/121732_2.png) [@Nuck](https://meta.discourse.org/u/Nuck)
#### Post date: [April 6, 2016, 9:29pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/8 "2016-04-06T21:29:09Z")

</div>

> [@eviltrout](#):
>
> I wouldn’t recommend doing it that way as it would likely break future compatibility. You can use this for now but I’ll try to introduce a workaround shortly that will be safer long term.

Makes sense, it’s kinda circumventing the whole Plugin API thing and relying on implementation details of the ES6 compilation output, both things that are Dangerous™

Anyways, I’ll definitely keep an eye out for a better solution

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [April 6, 2016, 9:29pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/9 "2016-04-06T21:29:37Z")

</div>

I’ll try to get to it soon and I’ll reply in this topic, so watch it and you’ll see 🙂

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [April 7, 2016, 8:18pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/10 "2016-04-07T20:18:29Z")

</div>

Actually thinking about it, the `decorateWidget` helper gets called with an object that has the `h` method. How are you inserting your widget if not via a decorator?

If you could post code that would be helpful.

---

<div class="post-metadata">

### Author: ![Nuck](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/nuck/32/121732_2.png) [@Nuck](https://meta.discourse.org/u/Nuck)
#### Post date: [April 7, 2016, 8:20pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/11 "2016-04-07T20:20:40Z")

</div>

> [@eviltrout](#):
>
> How are you inserting your widget if not via a decorator?

`{{mount-widget}}` in a template for a plugin outlet.

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [April 7, 2016, 8:30pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/12 "2016-04-07T20:30:27Z")

</div>

Ah that’s clever – I didn’t think people would try that. Okay, let me try something out.

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [April 7, 2016, 8:39pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/13 "2016-04-07T20:39:32Z")

</div>

Okay, I’ve added `h` to the `pluginApi` object as long as you request plugin api v0.3:

```html
<script type="text/discourse-plugin" version="0.3">
  console.log(api.h('b', ['hello', 'world']));
</script>

```

That should work for you!

[https://github.com/discourse/discourse/commit/b10b6c673d5f4936d0ee107e16531df106fae75e](https://github.com/discourse/discourse/commit/b10b6c673d5f4936d0ee107e16531df106fae75e)

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [May 25, 2025, 12:50pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/17 "2025-05-25T12:50:16Z")

</div>

> [@Discourse](#):
>
> `<script type="text/discourse-plugin" version="0.1">`

This is now deprecated as of:

> [@Modernizing inline script tags for templates & JS API](https://meta.discourse.org/t/modernizing-inline-script-tags-for-templates-js-api/366482):
>
> 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 Late September 2025 March 2026 - removal of feature Converting…

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [May 27, 2025, 3:19pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/18 "2025-05-27T15:19:58Z")

</div>

Updated here, and in a bunch of other version-controlled docs. Thanks for the heads-up @NateDhaliwal

---

<div class="post-metadata">

### Author: ![jenmck](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jenmck/32/332487_2.png) [@jenmck](https://meta.discourse.org/u/jenmck)
#### Post date: [November 15, 2025, 1:04am UTC](https://meta.discourse.org/t/using-the-js-api/41281/19 "2025-11-15T01:04:38Z")

</div>

> [@Discourse](#):
>
> All the available APIs are listed in the [`plugin-api.gjs` source code](https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.gjs) in Discourse core, along with a short description and examples.

This link is no longer working, I get this message:

> The `main` branch of discourse does not contain the path app/assets/javascripts/discourse/app/lib/plugin-api.gjs.

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [November 15, 2025, 1:58am UTC](https://meta.discourse.org/t/using-the-js-api/41281/20 "2025-11-15T01:58:26Z")

</div>

Ah, that’s because javascript files were moved to the `frontend/` directory instead of `app/assets/javascripts/`.

I have opened a PR:  
[https://github.com/discourse/discourse-developer-docs/pull/75](https://github.com/discourse/discourse-developer-docs/pull/75)

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [November 25, 2025, 11:25pm UTC](https://meta.discourse.org/t/using-the-js-api/41281/22 "2025-11-25T23:25:50Z")

</div>

Bumping this PR. Could it be reviewed? Thanks!

---

<div class="post-metadata">

### Author: ![MarkDoerr](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/markdoerr/32/549630_2.png) [@MarkDoerr](https://meta.discourse.org/u/MarkDoerr)
#### Post date: [January 30, 2026, 7:30am UTC](https://meta.discourse.org/t/using-the-js-api/41281/23 "2026-01-30T07:30:33Z")

</div>

Merged in December. 🎉

---

<div class="post-metadata">

### Author: ![MarkDoerr](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/markdoerr/32/549630_2.png) [@MarkDoerr](https://meta.discourse.org/u/MarkDoerr)
#### Post date: [January 30, 2026, 7:30am UTC](https://meta.discourse.org/t/using-the-js-api/41281/24 "2026-01-30T07:30:38Z")

</div>



[Next page](https://meta.discourse.org/t/using-the-js-api/41281.md?page=2)
