# How to customize routes and render custom pages in a plugin?

**URL:** https://meta.discourse.org/t/topic/391441
**Category:** Development
**Created:** [December 16, 2025, 8:49am UTC](https://meta.discourse.org/t/topic/391441 "2025-12-16T08:49:53Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![singi2016cn](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/singi2016cn/32/532755_2.png) [@singi2016cn](https://meta.discourse.org/u/singi2016cn)
#### Post date: [December 16, 2025, 8:49am UTC](https://meta.discourse.org/t/topic/391441/1 "2025-12-16T08:49:53Z")

</div>

In a plugin, how do I customize routes and then render a custom page?

I looked at this post: [Developing Discourse Plugins - Part 5 - Add an Admin Interface - Docs / Developer Guide - Discourse Meta](https://meta.discourse.org/t/developing-discourse-plugins-part-5-add-an-admin-interface/31761?silent=true), and other plugins, but they all failed.

My custom routes are returning JSON data, but what I want is to render an Ember template. Is there a post that explains this related knowledge? Thank you very much.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [December 16, 2025, 8:58am UTC](https://meta.discourse.org/t/topic/391441/2 "2025-12-16T08:58:35Z")

</div>

If that’s all you want to do, take a look at:

> [@Landing Pages Plugin small\_airplane](https://meta.discourse.org/t/landing-pages-plugin/180967):
>
> The Landing Pages Plugin lets you make standalone landing pages for your Discourse instance. You can display topics, users and other content from your forum. You can restrict pages to groups, apply Discourse themes, set custom paths, and manage pages in a dedicated git repository. [[Screen Shot 2021-02-23 at 6.11.10 PM]](https://thepavilion.io/welcome)desktop_computer[Get the code](https://github.com/paviliondev/discourse-landing-pages)page_facing_up [Read the documentation](https://coop.pavilion.tech/docs?category=130)raising_hand_woman [Request a feature](https://coop.pavilion.tech/c/support/discourse-landing-pages/131)bug [Report a bug](https://coop.pavilion.tech/c/support/discourse-landing-pages/131) Not sure how to install a plugin? [Follow t…](https://meta.discourse.org/t/install-plugins-in-discourse/19157)

And:

> [@Landing Pages Plugin small\_airplane](https://meta.discourse.org/t/landing-pages-plugin/180967/95):
>
> Just to point out you can now display arbitrary pages within Discourse using the Landing Pages Plugin in combination with a couple of extensions:

---

<div class="post-metadata">

### Author: ![singi2016cn](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/singi2016cn/32/532755_2.png) [@singi2016cn](https://meta.discourse.org/u/singi2016cn)
#### Post date: [December 16, 2025, 9:35am UTC](https://meta.discourse.org/t/topic/391441/4 "2025-12-16T09:35:03Z")

</div>

I am currently stuck here, here are my specific steps:

I defined the route in the file `plugins/q/config/routes.rb`:

```plaintext
get "/hello" => "examples#hello"

```

Controller: `plugins/q/app/controllers/quectel/examples_controller.rb`

```plaintext
def hello
   render json: { hello: "world" }
end

```

Now visiting: [localhost:4200/q/hello](http://localhost:4200/q/hello), returns json

```plaintext
{"hello":"world"}

```

How can I render the Ember template when visiting [localhost:4200/q/q/hello](http://localhost:4200/q/hello)?

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [December 16, 2025, 10:02am UTC](https://meta.discourse.org/t/topic/391441/5 "2025-12-16T10:02:30Z")

</div>

I bookmarked this topic some time ago to read later: [Creating Routes in Discourse and Showing Data](https://meta.discourse.org/t/creating-routes-in-discourse-and-showing-data/48827). Not sure if it’s up-to-date, but maybe it’ll still help.

---

<div class="post-metadata">

### Author: ![singi2016cn](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/singi2016cn/32/532755_2.png) [@singi2016cn](https://meta.discourse.org/u/singi2016cn)
#### Post date: [December 16, 2025, 10:13am UTC](https://meta.discourse.org/t/topic/391441/7 "2025-12-16T10:13:51Z")

</div>

Yes, I also read this post and followed the steps above, but it didn’t work, and an “Page not found” error appeared. Then, I was confused.

---

<div class="post-metadata">

### Author: ![singi2016cn](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/singi2016cn/32/532755_2.png) [@singi2016cn](https://meta.discourse.org/u/singi2016cn)
#### Post date: [December 17, 2025, 8:57am UTC](https://meta.discourse.org/t/topic/391441/8 "2025-12-17T08:57:27Z")

</div>

OK, I roughly achieved the desired effect by studying the code for `plugins/discourse-subscriptions`.

Visiting `/q/hello` displays the custom template content and fetches custom backend data.

Here are my steps:

1. Create a new path mapping file `plugins/quectel/assets/javascripts/discourse/quectel-route-map.js`

```javascript
export default function quectel() {
  this.route("q/hello"); // 路由默认是/q/hello
};

```

1. Create a new route file `plugins/quectel/assets/javascripts/discourse/routes/q/hello.js`

```javascript
import Route from "@ember/routing/route";
import { ajax } from "discourse/lib/ajax";

export default class HelloRoute extends Route {
  model() {
    return ajax('/q/hello') // 请求后端接口，获取数据
  }
}

```

1. Create a frontend template file `plugins/quectel/assets/javascripts/discourse/templates/q/hello.gjs` to render the data

```glimmer
import QHello from '../../components/q-hello'

<template>
  <h1>q-hello</h1>
  {{!-- 渲染组件 --}}
  <QHello />
  {{!-- 渲染模型数据 --}}
  <div>{{@model.hello}}</div>
</template>

```

1. Create a backend controller file `plugins/quectel/app/controllers/quectel/examples_controller.rb`

```ruby
# frozen_string_literal: true

module ::Quectel
  class ExamplesController < ::ApplicationController
    requires_plugin PLUGIN_NAME

    def hello
      render json: { hello: "hello 2333" }
    end
  end
end

```

1. Define the backend route in `plugins/quectel/config/routes.rb`

```ruby
# frozen_string_literal: true

Quectel::Engine.routes.draw do
  get "/hello" => "examples#hello"
end

Discourse::Application.routes.draw { mount ::Quectel::Engine, at: "q" }

```

Visiting `/q/hello` will show the following display:

 ![image](https://global.discourse-cdn.com/meta/original/4X/3/c/f/3cf2cfd088d3b0dd1b44c43d7a6d0e6599e40bee.png)
