diff --git a/app/assets/javascripts/discourse/components/mount-widget.js.es6 b/app/assets/javascripts/discourse/components/mount-widget.js.es6
index 3b0c22f89..b64625d03 100644
--- a/app/assets/javascripts/discourse/components/mount-widget.js.es6
+++ b/app/assets/javascripts/discourse/components/mount-widget.js.es6
@@ -18,6 +18,7 @@ export default Ember.Component.extend({
   init() {
     this._super();
     this._widgetClass = this.container.lookupFactory(`widget:${this.get('widget')}`);
+    this._connected = [];
   },
 
   didInsertElement() {
@@ -33,6 +34,9 @@ export default Ember.Component.extend({
     if (callbacks) {
       callbacks.forEach(cb => cb());
     }
+
+    this._connected.forEach(v => v.destroy());
+    this._connected.length = 0;
   },
 
   willDestroyElement() {
diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6
index a012eb55e..49db0a298 100644
--- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6
+++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6
@@ -84,7 +84,9 @@ class PluginApi {
    * ```
    **/
   addPosterIcon(cb) {
-    decorateWidget('poster-name:after', (h, attrs) => {
+    decorateWidget('poster-name:after', dec => {
+      const attrs = dec.attrs;
+
       const result = cb(attrs.userCustomFields || {}, attrs);
       if (result) {
         let iconBody;
@@ -94,7 +96,7 @@ class PluginApi {
         } else if (result.emoji) {
           iconBody = result.emoji.split('|').map(emoji => {
             const src = Discourse.Emoji.urlFor(emoji);
-            return h('img', { className: 'emoji', attributes: { src } });
+            return dec.h('img', { className: 'emoji', attributes: { src } });
           });
         }
 
@@ -103,13 +105,13 @@ class PluginApi {
         }
 
         if (result.url) {
-          iconBody = h('a', { attributes: { href: result.url } }, iconBody);
+          iconBody = dec.h('a', { attributes: { href: result.url } }, iconBody);
         }
 
 
-        return h('span',
-                 { className: result.className, attributes: { title: result.title } },
-                 iconBody);
+        return dec.h('span',
+                     { className: result.className, attributes: { title: result.title } },
+                     iconBody);
       }
     });
   }
diff --git a/app/assets/javascripts/discourse/widgets/connector.js.es6 b/app/assets/javascripts/discourse/widgets/connector.js.es6
new file mode 100644
index 000000000..c132666a4
--- /dev/null
+++ b/app/assets/javascripts/discourse/widgets/connector.js.es6
@@ -0,0 +1,39 @@
+export default class Connector {
+
+  constructor(widget, opts) {
+    this.widget = widget;
+    this.opts = opts;
+  }
+
+  init() {
+    const $elem = $(`<div class='widget-connector'></div>`);
+    const elem = $elem[0];
+
+    const { opts, widget } = this;
+    Ember.run.next(() => {
+
+      const mounted = widget._findView();
+
+      let context;
+      if (opts.context === 'model') {
+        const model = widget.findAncestorModel();
+        context = model;
+      }
+
+      const view = Ember.View.create({
+        container: widget.container,
+        templateName: opts.templateName,
+        context
+      });
+      mounted._connected.push(view);
+
+      view.renderer.replaceIn(view, $elem[0]);
+    });
+
+    return elem;
+  }
+
+  update() { }
+}
+
+Connector.prototype.type = 'Widget';
diff --git a/app/assets/javascripts/discourse/widgets/widget.js.es6 b/app/assets/javascripts/discourse/widgets/widget.js.es6
index 56c025fde..680a7b594 100644
--- a/app/assets/javascripts/discourse/widgets/widget.js.es6
+++ b/app/assets/javascripts/discourse/widgets/widget.js.es6
@@ -1,5 +1,6 @@
 import { WidgetClickHook, WidgetClickOutsideHook } from 'discourse/widgets/click-hook';
 import { h } from 'virtual-dom';
+import Connector from 'discourse/widgets/connector';
 
 function emptyContent() { }
 
@@ -16,14 +17,33 @@ export function renderedKey(key) {
 
 const _decorators = {};
 
+class DecoratorHelper {
+  constructor(container, attrs, state) {
+    this.container = container;
+    this.attrs = attrs;
+    this.state = state;
+  }
+
+  connect(details) {
+    return new Connector(this.container, details);
+  }
+}
+DecoratorHelper.prototype.h = h;
+
 export function decorateWidget(widgetName, cb) {
   _decorators[widgetName] = _decorators[widgetName] || [];
   _decorators[widgetName].push(cb);
 }
 
-function applyDecorators(name, type, attrs, state) {
-  const decorators = _decorators[`${name}:${type}`] || [];
-  return decorators.map(d => d(h, attrs, state));
+function applyDecorators(widget, type, attrs, state) {
+  const decorators = _decorators[`${widget.name}:${type}`] || [];
+
+  if (decorators.length) {
+    const helper = new DecoratorHelper(widget, attrs, state);
+    return decorators.map(d => d(helper));
+  }
+
+  return [];
 }
 
 function drawWidget(builder, attrs, state) {
@@ -58,8 +78,8 @@ function drawWidget(builder, attrs, state) {
 
   let contents = this.html(attrs, state);
   if (this.name) {
-    const beforeContents = applyDecorators(this.name, 'before', attrs, state) || [];
-    const afterContents = applyDecorators(this.name, 'after', attrs, state) || [];
+    const beforeContents = applyDecorators(this, 'before', attrs, state) || [];
+    const afterContents = applyDecorators(this, 'after', attrs, state) || [];
     contents = beforeContents.concat(contents).concat(afterContents);
   }