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

{{/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.