Handlebars templates

Looona uses a dual-template system powered by Handlebars to format reports for both the dashboard and external services.

Supported services

Looona currently supports the following external services for forwarding reports:

GitHub Issues

Create issues directly in your GitHub repositories

Slack

Post messages to Slack channels (Mrkdwn or Block Kit)

Discord

Send messages to Discord channels

Jira

Create tickets in Jira projects

About Looona services

Looona relies on a companion service called looona-services to handle:

  • OAuth authentication flows for external services (GitHub, Slack, Jira)
  • Automatic template conversion from Markdown to service-specific formats

Future plans: We're working towards making looona-services optional. You'll be able to configure your own custom endpoints for complete control over your integration, enabling a fully self-hosted setup without external dependencies.

Template system overview

When you create a Reporter in Looona, you define two templates that control how your feedback reports are displayed and forwarded:

Local template (Markdown)

Used to display reports in the Looona dashboard. Always in Markdown format, providing a consistent, service-independent view of your reports.

Remote template (service-specific)

Used to format reports when forwarding to external services like GitHub, Slack, Jira, or Discord. The format depends on the service (e.g., Slack Block Kit, Atlassian Document Format, Markdown for GitHub/Discord).

Automatic conversion

Looona can automatically convert your local template (Markdown) to the appropriate remote template format for your chosen service. However, this conversion is not 100% reliable, especially for complex templates with conditional logic.

For production use, we recommend manually editing your remote template to ensure it matches your exact requirements.

Handlebars template syntax

Both local and remote templates use Handlebars syntax to dynamically insert data from your reports. For complete documentation, visit the official Handlebars documentation.

Basic variables

All fields from your report's content JSON are available as Handlebars variables:

{{description}}
{{steps}}
{{rating}}
{{userName}}

Nested properties

Access nested JSON properties using dot notation:

{{userInformations.userId}}
{{userInformations.email}}
{{userInformations.plan}}

Conditional logic

Use if statements to show content conditionally:

{{#if additionalComment}}
šŸ’¬ **Comment**
{{additionalComment}}
{{/if}}

Custom helpers

Looona provides custom Handlebars helpers for comparisons:

{{#if (eq severity "critical")}}šŸ”“{{/if}}
{{#if (gt rating 4)}}⭐ Excellent!{{/if}}
{{#if (lt rating 3)}}āš ļø Needs improvement{{/if}}

Available helpers: eq (equals), gt (greater than), lt (less than)

Local template example (Markdown)

Here's an example of a local template for displaying bug reports in the Looona dashboard:

# šŸ› Bug Report

## šŸ“‹ General information
- **Severity:** {{severity}} {{#if (eq severity "critical")}}šŸ”“{{/if}}{{#if (eq severity "high")}}🟠{{/if}}
- **Affected page:** `{{page}}`
- **Date:** {{timestamp}} šŸ“…

## šŸ“ Description
{{description}}

## šŸ‘¤ User context
- **User ID:** {{userInformations.userId}}
- **Email:** {{userInformations.email}} šŸ“§
- **Plan:** {{userInformations.plan}} šŸ’Ž

{{#if screenshotUrl}}
## šŸ“ø Screenshot
![Screenshot]({{screenshotUrl}})
{{/if}}

This template would be populated with data from your report's content JSON field.

Remote template examples

Remote templates vary by service. Here are examples for different platforms:

GitHub Issues / Discord (Markdown)

For GitHub and Discord, the remote template is also Markdown, so it can be identical to your local template.

## šŸ› Bug Report

**Severity:** {{severity}} {{#if (eq severity "critical")}}šŸ”“{{/if}}

{{description}}

**Reported by:** {{userInformations.email}}

Slack (Mrkdwn format)

Slack uses a simplified Markdown format called Mrkdwn. Learn more in the Slack formatting documentation.

šŸ‘šŸ‘Ž Binary Feedback

_Rating:_ {{#if (eq value "positive")}}šŸ‘ Positive{{else}}šŸ‘Ž Negative{{/if}}
_Target:_ {{target}} šŸŽÆ

{{#if additionalComment}}šŸ’¬ Comment

{{additionalComment}}{{/if}}

---
_{{timestamp}}_ šŸ“…

Slack (Block Kit JSON)

For richer formatting, Slack supports Block Kit JSON. Explore all blocks in the Block Kit documentation.

[
  {
    "type": "header",
    "text": { "type": "plain_text", "text": "šŸ› Bug Report" }
  },
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "• *Severity:* {{severity}}\n• *Page:* `{{page}}`"
    }
  },
  {
    "type": "section",
    "text": { "type": "mrkdwn", "text": "{{description}}" }
  }
]

Jira (Atlassian Document Format)

Jira uses the Atlassian Document Format (ADF), a JSON-based structure. Learn more in the ADF documentation.

{
  "type": "doc",
  "version": 1,
  "content": [
    {
      "type": "heading",
      "attrs": { "level": 1 },
      "content": [{ "type": "text", "text": "šŸ› Bug Report" }]
    },
    {
      "type": "paragraph",
      "content": [
        { "type": "text", "text": "Severity: ", "marks": [{ "type": "strong" }] },
        { "type": "text", "text": "{{severity}}" }
      ]
    }
  ]
}

Jira custom fields

When using Jira as an external service, you can populate custom fields by adding a fields property in the report's content. These values are passed directly to the Jira API when creating the ticket.

Example content with custom fields

{
  "summary": "Bug: Login button not working",
  "description": "Users cannot click the login button",
  "priority": "High",
  "fields": {
    "customfield_10001": "Mobile App",
    "customfield_10002": "v2.3.1",
    "customfield_10003": { "value": "Bug" }
  }
}

Important

The values inside the fields object are passed directly to the Jira API. Make sure to use the correct format for each field type. Refer to your Jira project's field configuration for the expected format.

Structuring your content field

When submitting reports via the API, the content field must be a stringified JSON object that matches the variables used in your templates.

Example: bug report content

{
  "severity": "high",
  "description": "The app crashes when uploading files",
  "page": "/upload",
  "timestamp": "2024-01-15 10:30:00",
  "userInformations": {
    "userId": "user_123",
    "email": "[email protected]",
    "plan": "premium"
  },
  "userAgent": "Mozilla/5.0..."
}

When sending via API, stringify this JSON:

"content": "{\"severity\": \"high\", \"description\": \"The app crashes...\"}"

Using the extra field

Data you don't want to display in templates should go in the extra field instead of content:

"content": "{\"description\": \"Bug report\"}",
"extra": "{\"internalId\": \"BUG-1234\", \"metadata\": \"...\"}"

Automatic conversion limitations

Known issues

While automatic conversion from Markdown to service-specific formats is convenient, it has limitations:

  • Conditional logic: JSON-based formats (Slack Block Kit, ADF) may not handle Handlebars {{#if}} statements correctly.
  • Complex formatting: Advanced Markdown features may not convert perfectly to all target formats.
  • Service-specific features: Some platform-specific features (like Slack interactive buttons) can only be added manually.

Recommendation: Use automatic conversion as a starting point, then manually refine your remote template for production use.

Best practices

Keep templates simple

Start with simple templates and gradually add complexity. Simpler templates are more reliable across automatic conversions.

Test with sandbox mode

Use isTest: true when testing templates to avoid creating real issues/messages in your external services. Learn more in the API reference.

Document your content structure

Maintain documentation of what fields your templates expect, making it easier for developers to submit correctly formatted reports.

Ready to deploy?

Now that your templates are configured, deploy Looona to production and make it available to your users.

Feedback