Beginner's Guide to Creating Discourse Plugins - Part 1


(Jithin Krishnan) #67

What is the best way to modify Discourse Database from plugin?
I am developing a Discourse plugin that need to create a new table and store some data.
Also it needs to fetch content from my server. Could you suggest the best way to do this?

(Mittineague) #68

Hi Jithin_K_M welcome to the forum

I don’t know what exactly you have in mind to do.
But IMHO it would most likely be best for you to use the existing “custom fields” tables. i.e.


(Jithin Krishnan) #69

Thanks Mittineague for your quick reply :slight_smile:

Could you suggest some docs where I can find some snippets for using these custom fields within my plugin?

(Mittineague) #70

Unfortunately I don’t think it there is specific documentation yet.

What I usually do is study other plugins that work and try to pick out what I want to do from them. I keep in mind that by this “monkey see - monkey do” pragmatic approach I may be learning less than best practice, but I trust that the Discourse team knows better than I do.

I know these 3 plugins use custom fields, are “official”, and should be OK to use as a learning source.

Staff Notes
GitHub - discourse/discourse-staff-notes: Plugin for Staff users to create notes on users

Solved (aka Accepted Answer)
GitHub - discourse/discourse-solved: Allow accepted answers on topics

discourse/plugins/poll at master · discourse/discourse · GitHub

(Jithin Krishnan) #71

Thanks for your suggestions. It would be great if there is an official documentation on plugin development (assessing admin settings, database etc.)

(Mittineague) #72

If you work your way through the tutorials you will gain some valuable “hands on” experience.

And this is good

(Jithin Krishnan) #73

Thanks a lot Mittineague :slight_smile:

I previously started following the Beginner’s Guide, but my plugin was not loaded (might be caching issue).
Tried again now and got it working!
Let me go through the complete guide.

(Jithin Krishnan) #77

What is the best way to render ajax response inside a widget?
I was able to console.log the ajax response, but it is not rendering inside the widget.

Tried to call this.scheduleRerender() after getting ajax response but both results in an infinite loop.

(Jithin Krishnan) #78

I am getting following error when trying to load a helper module inside my widget

Could you please help me to resolve this error?

(Robin Ward) #79

You should make sure your widget can contain state. After the ajax request, set it on the state object and trigger a this.scheduleRerender and it should appear. For an example look at how the post menu shows who liked something.

Looks like you have the wrong path to your helper. One way to see all the paths that Discourse has resolved is by typing require._eak_seen in your console. Look for the correct path name.

(Jithin Krishnan) #80

Thanks Robin Ward for your help. I was able to load my module after investigating with require._eak_seen.

Below is the code for my widget. The problem is that this.scheduleRerender() is causing an infinite loop. The div always shows loading animation (even without this.scheduleRerender) which seems this.state.loading is not being set.

import { createWidget } from 'discourse/widgets/widget';
import { getTopic } from 'discourse/plugins/my-plugin/discourse/helpers/topics';
import { ajax } from 'discourse/lib/ajax';
import { h } from 'virtual-dom';

export default createWidget('topic-widget', {
  tagName: '',
  defaultState() {
    return { loading: false};

  refreshTopic() {
    if (this.state.loading) { return; }
    this.state.loading = true;
    this.state.topic = 'empty';
    getTopic(this).then((result) => {
      this.state.topic = result;
      this.state.loading = false;

  html(attrs, state) {
    if (!state.topic) {
    const result = [];
    if (state.loading) {
      result.push(h('div.spinner-container', h('div.spinner')));
    } else if (state.topic !== 'empty') {
    } else {
      result.push(h('', 'No topic.'))

    return result;

Could you help to resolve this error?

(Robin Ward) #81

Your code looks mostly fine, however, all widgets that deal with state require a key attribute. You should have seen a warning about this, although maybe the warning only appears when running tests?

Try adding a buildKey function like buildKey: () => 'topic-widget'.

That should probably fix it. Also, a much less serious issue is you might want to add topic: null to your state object. Javascript is much faster when it knows the shape of the object in advance.

(Jithin Krishnan) #82

Thanks Robin Ward.
Adding buildKey function worked fine for me.

(Alan Tan) #83

@eviltrout I got bitten by this in development and didn’t get a warning about setting a key attribute.

(Robin Ward) #84

Ah, it only warns in Ember.testing:

We might want to just raise an error if that happens now? It is pretty dangerous for Widgets to do that.

(Alan Tan) #85

:thumbsup: for raising an error instead. Maybe we can extend that to development too? :thought_balloon:

(Robin Ward) #86

Hopefully this will prevent others from making this mistake:

(Sudaraka Jayathilaka) #87


Is this url correct? Shouldn’t this be ,

I’m new here. please correct me if im wrong :slight_smile:

(Vinoth Kannan) #88

In Discourse, mainly there are two big JavaScript sections “discourse” and “admin”. Also you can see few more in discourse/app/assets/javascripts at master · discourse/discourse · GitHub.

In plugins, to differentiate admin & normal user section JavaScript files we use “discourse” and “admin” keywords in between like below


Also it will just work even without identification keywords like you mentioned :slight_smile:

(Sudaraka Jayathilaka) #89

@vinothkannans Thank you a lot for the explanation. :slight_smile: