# Developing Discourse Plugins - Part 1 - Create a basic plugin

**URL:** https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515
**Category:** Developer Guides
**Tags:** plugin-guides, tutorial
**Created:** [June 26, 2015, 9:01pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515 "2015-06-26T21:01:08Z")
**Posts on this page:** 13
**Page:** 2

<div class="post-metadata">

### Author: ![pacharanero](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pacharanero/32/500583_2.png) [@pacharanero](https://meta.discourse.org/u/pacharanero)
#### Post date: [April 4, 2019, 12:04pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/109 "2019-04-04T12:04:39Z")

</div>

> [@sam](#):
>
> I am totally open to a PR that automatically follows symlinks in `plugins/` dir and then smart mounts volumes.

Hmmmm. That’s a tricky one, because the symlink needed to be deleted, otherwise the Docker mount fails (as it cannot overwrite the existing file).

I had a bit of a google around for solutions, and found an interesting solution which would dereference the symlink and **copy** the symlink’s target into the specified directory, but couldn’t find anything that would create a Docker volume mount, thus preserving the dynamic link and bi-directional synchronisation. Happy to be steered towards a solution as this would be a neat feature to have.

---

<div class="post-metadata">

### Author: ![Dev\_Work](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dev_work/32/124200_2.png) [@Dev\_Work](https://meta.discourse.org/u/Dev_Work)
#### Post date: [December 26, 2019, 8:31am UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/111 "2019-12-26T08:31:07Z")

</div>

Tell me how to add your validation to the app/models /user.rb model?

> <https://github.com/discourse/discourse/blob/8c2e27790cf7c5fcc883489a7bdf36008fda88ac/app/models/user.rb#L99-L110>

---

<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: [December 27, 2019, 2:15pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/112 "2019-12-27T14:15:39Z")

</div>

You can use regular Ruby code to do this in your `plugin.rb`:

```ruby
class ::User < ActiveRecord::Base
  validates_presence_of :your_attribute
end

```

---

<div class="post-metadata">

### Author: ![AstonJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/astonj/32/215041_2.png) [@AstonJ](https://meta.discourse.org/u/AstonJ)
#### Post date: [January 29, 2020, 3:25am UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/113 "2020-01-29T03:25:38Z")

</div>

Just starting on my first small plugin 😊 I have a few questions I hope someone can help with…

> [@eviltrout](#):
>
> > 🎉 **Update August 2018** , see [Rails plugin generator](https://meta.discourse.org/t/rails-plugin-generator/95907) to create a complete discourse plugin skeleton in your plugins directory 🎉

First question is does it matter if we use what’s in this guide or should we really be aiming to use the plugin generator?

> [@eviltrout](#):
>
> The `plugin.rb` file has two purposes: it is the manifest for your plugin with the required information about your plugin including: its name, contact information and a description. **The second purpose is to initialize any ruby code necessary to run your plugin.**

It wasn’t covered in this guide, but how can we make data available to our templates from the plugin.rb file?

Let’s say for example I want to display a random welcome message at the plugin-outlet `topic-above-posts` whenever somebody visits a topic, with the message being randomly selected from an array in `plugin.rb`… how would I send that data to the template? Which I also presume would go here:

`plugins/my_new_plugin/assets/javascripts/discourse/templates/connectors/topic-above-posts/my_new_plugin.hbs` (Is this correct?)

Any tips greatly appreciated 😊

---

<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: [January 29, 2020, 3:42pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/114 "2020-01-29T15:42:38Z")

</div>

Using the plugin generator is generally a great way to get started and see how a plugin should be structured. I recommend it.

Now, regarding how to get ruby code into the front end: those are two different applications. The front end application (Ember) will have to request it from the server (Rails) somehow. Normally we do this via an AJAX call, but you could also do something like add to the SiteSerializer so that it’s sent automatically as part of the forum data.

Otherwise you will have to look up how to add a route/controller in rails and send the messages as JSON.

---

<div class="post-metadata">

### Author: ![AstonJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/astonj/32/215041_2.png) [@AstonJ](https://meta.discourse.org/u/AstonJ)
#### Post date: [January 29, 2020, 7:24pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/115 "2020-01-29T19:24:09Z")

</div>

Thanks Robin!

Would you know if there are any tutorials on this? Or, is there a simple plugin (or even a dummy plugin) we can look at that points us in the right direction? (If not, is this something you could quickly put up for us somewhere please? I think it’ll help a lot of people 😊)

_Plus.. any more thoughts on writing a book? I can put you in touch with someone if you fancy it 😃_

---

<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: [January 30, 2020, 3:06pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/116 "2020-01-30T15:06:29Z")

</div>

Thanks for the compliment but I’m definitely far too busy to consider writing a book. Plus tech books have SUPER limited range. Those I know who have written them say you do it for yourself, not for the money 🙂

I can’t think of simple plugin for returning something from the server side. [discourse-tooltips](https://github.com/discourse/discourse-tooltips/blob/master/plugin.rb#L15) adds a route and then uses that to get previews of topics as you mouseover so that might be helpful.

---

<div class="post-metadata">

### Author: ![neounix](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/neounix/32/215617_2.png) [@neounix](https://meta.discourse.org/u/neounix)
#### Post date: [March 19, 2020, 2:37pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/118 "2020-03-19T14:37:54Z")

</div>

Soon, I will start plugin development…

Question:

Is there a list of `hooks` where we actually can `plugin`?

For example, say we want to write a plugin to process the `cooked` data in a `post` before it is displayed, we would expect there to be at least one `hook` at the beginning and one at the end, like (for example):

- display\_post\_start

- display\_post\_complex

Then, our `plugin` would `hook` into the code at the `hook location` of our choice above.

Is there a list of these `plugin hooks` and what are these `plugin hooks` called in `DiscourseWorld`?

Update: Found this:

```plaintext
git grep "plugin-outlet" -- "*.hbs"

```

But there was no `outlet` for modifying cooked post content, that I can see.

Is there a tutorial on creating our own outlets, for example, an `outlet` to modify the `cooked` part of posts?

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [March 19, 2020, 6:11pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/119 "2020-03-19T18:11:03Z")

</div>

You’re looking for this guide, section `decorateCooked()`:

> [@Developing Discourse Themes & Theme Components](https://meta.discourse.org/t/developer-s-guide-to-discourse-themes/93648#heading--4-c-12):
>
> Discourse Themes and Theme Components can be used to customize the look, feel and functionality of Discourse’s frontend. This section of the developer guides aims to provide all the reference materials you need to develop simple themes for a single site, right up to complex open-source theme components. This introduction aims to provide a map of all the tools and APIs for theme development. If you prefer a step-by-step tutorial for theme development, jump straight to: Themes vs. Theme Compon…

---

<div class="post-metadata">

### Author: ![AquaL1te](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/aqual1te/32/201966_2.png) [@AquaL1te](https://meta.discourse.org/u/AquaL1te)
#### Post date: [December 8, 2023, 8:25am UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/133 "2023-12-08T08:25:29Z")

</div>

I use this skeleton for a custom plugin. Do I need to change anything for the upcoming Ember 5 transition?

> [@Preparing for Discourse's upgrade to Ember 5](https://meta.discourse.org/t/preparing-for-discourses-upgrade-to-ember-5/287211):
>
> Discourse will soon be upgrading our version of the Ember Javascript framework from version 3 to version 5. The vast majority of the preparation work is done, and many sites are already running on Ember 5 (including Meta!). ember This upgrade is available behind a feature flag right now! This is the default for self-hosted Discourse installations starting 10th January. Initially, Ember 3 support will remain available via a flag, but will be removed within the first quarter of 2024. On our ma…

I suppose not? Although this is just a linter?

> <https://github.com/discourse/discourse-plugin-skeleton/blob/a92be1d2aa26c96e45ada26e43033dde38128a4f/package.json#L9>

---

<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: [December 8, 2023, 10:18am UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/134 "2023-12-08T10:18:37Z")

</div>

The skeleton is compatible with Ember 5. But if you added any javascript code or hbs templates, you’ll need to check for deprecations as described in the announcement.

> [@AquaL1te](#):
>
> Although this is just a linter?

Yeah the stuff in package.json is just related to linting, it doesn’t affect the functionality or compatibility of a plugin.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [September 24, 2024, 6:11pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/136 "2024-09-24T18:11:35Z")

</div>

> [@Discourse](#):
>
> 🎉 Use [GitHub - discourse/discourse-plugin-skeleton: Template for Discourse plugins](https://github.com/discourse/discourse-plugin-skeleton) to create a complete discourse plugin skeleton in your plugins directory 🎉

Please add to this

```plaintext
rake plugin:create[plugin-name]

```

I spent too long today trying to find [Automating Discourse Plugin Setup with `create-discourse-plugin` gem](https://meta.discourse.org/t/automating-discourse-plugin-setup-with-create-discourse-plugin-gem/315013) (not sure how I was unable to remember that this was the name of the rake task?! 🤷 )

---

<div class="post-metadata">

### Author: ![Ambient.Impact](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ambient.impact/32/387027_2.png) [@Ambient.Impact](https://meta.discourse.org/u/Ambient.Impact)
#### Post date: [July 8, 2025, 8:52pm UTC](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515/141 "2025-07-08T20:52:10Z")

</div>

I would also add a note when using the Docker set up as the instructions above will fail with an error similar to

```plaintext
ambientimpact:~/.../Discourse/discourse$ d/rake plugin:create[neurocracy-date-rewrite]
Cloning 'https://github.com/discourse/discourse-plugin-skeleton' to '/src/plugins/neurocracy-date-rewrite'...
Initializing git repository...
Initialized empty Git repository in /src/plugins/neurocracy-date-rewrite/.git/
Author identity unknown

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: empty ident name (for <discourse@localhost>) not allowed
rake aborted!
Command failed with exit 128: git
/src/lib/tasks/plugin.rake:354:in `system'
/src/lib/tasks/plugin.rake:354:in `block (2 levels) in <main>'
/src/lib/tasks/plugin.rake:344:in `chdir'
/src/lib/tasks/plugin.rake:344:in `block in <main>'
Tasks: TOP => plugin:create
(See full trace by running task with --trace)

```

You first need to set up your `.gitconfig` inside the container (change email and name as needed) before trying to create the plug-in:

```shell
d/exec git config --global user.email "you@example.com"
d/exec git config --global user.name "Your Name"
d/rake plugin:create[plugin-name]

```

[Previous page](https://meta.discourse.org/t/developing-discourse-plugins-part-1-create-a-basic-plugin/30515.md?page=1)
