This page specifies the .rmtemplate package format, for template designers and tools that want to produce files Klass-RM Uploader can upload. For how to upload one, see Custom Templates.
.rmtemplate is a Klass Concepts packaging format. It is not a reMarkable format, and reMarkable does not read .rmtemplate files directly. Klass-RM Uploader unpacks the package and sends its template to your reMarkable account as a native cloud template.
Container
A .rmtemplate file is a standard ZIP archive with the file extension .rmtemplate.
- Entries may be stored (method 0) or DEFLATE-compressed (method 8). Other compression methods and encrypted entries are not supported.
- Each entry may be at most 256 MB uncompressed.
- Member names are matched exactly and are case-sensitive. Both members below must be at the archive root, not inside a folder. Zipping a folder with Finder puts the files one level down, which makes the package invalid.
- Any other members are ignored.
| Member | Required | Contents |
|---|---|---|
template.json |
Yes | The reMarkable template itself, in reMarkable's JSON template format. |
manifest.json |
No | Package information: a display name and a stable package identifier. |
There is no separate icon or thumbnail file. The template picker icon is carried inside template.json (see below).
template.json
template.json is a reMarkable vector template (formatVersion 1), the same JSON format the tablet uses for its own templates. Its bytes are sent to your reMarkable account exactly as they appear in the package; Klass-RM Uploader never rewrites or reformats them.
Klass-RM Uploader checks only the following before uploading. Anything beyond this is up to the tablet.
- The file must be valid JSON, and its top level must be an object.
namemust be present and must be a string.itemsmust be present. This is the template's drawing content.
A package that fails these checks is rejected with an error rather than uploaded. Fields worth getting right for the tablet:
categories- a list of template picker tabs. Include at least one (for example"All"). A template with an empty list syncs to the account but does not appear in any picker tab.iconData- the picker icon, as a base64-encoded SVG.orientation-"portrait"or"landscape".formatVersion(write1),author, andlabels(a list, may be empty).
template.json authoring basics
reMarkable has not published a specification for its template format. This section is a practical introduction based on our own testing on reMarkable tablets. It covers enough to build useful templates, and it may not match every firmware version. When in doubt, test on your device.
Top-level fields
| Field | Type | Notes |
|---|---|---|
formatVersion | number | Always 1. |
name | string | Picker name (unless manifest.json overrides it). |
author | string | Your name or company. |
templateVersion | string | Your own version number, such as "1.0.0". |
orientation | string | "portrait" or "landscape". |
categories | list of strings | Picker tabs. Use at least one; "All" is a safe choice. |
labels | list of strings | Optional tags. May be empty. |
iconData | string | Base64-encoded SVG shown in the picker. Use a 150 x 200 canvas for portrait, 200 x 150 for landscape. Simple black-and-white line art reads best. |
constants | list | Optional named values. See below. |
items | list | What gets drawn. See below. |
Coordinates and page size
The origin is the top-left corner of the page, and y increases downward. Units are page pixels. Instead of hard-coding a page size, use the variables templateWidth and templateHeight, which the tablet fills in when it draws the page. Portrait sizes we have seen:
- reMarkable 2: 1404 x 1872
- reMarkable Paper Pro: 1620 x 2160
- reMarkable Paper Pro Move: 820 x 1458
A template written against templateWidth and templateHeight adapts to every device. One built from fixed numbers for a reMarkable 2 will look wrong on the others.
Numbers and expressions
Any coordinate, size, repeat count, or visible value can be a plain number or a string containing an expression:
- Arithmetic with
+ - * /and parentheses:"templateWidth - margin * 2" - Comparisons,
&&,||, and the conditionalcondition ? a : b:"templateWidth > 1000 ? 120 : 60" - Names: the page variables above, your constants, and inside a group,
parentWidthandparentHeight
Constants
constants is a list of objects with one key each, not a single object. Each one can use the page variables and any constant defined above it:
"constants": [
{ "margin": 100 },
{ "lineSpacing": 64 },
{ "contentWidth": "templateWidth - margin * 2" }
]
Items
There are three item types: group, path, and text. Any item can have an id (a label for your own reference) and a visible value; an expression that evaluates to 0 hides the item.
group positions and repeats other items.
boundingBox:{ "x", "y", "width", "height" }. Children are positioned relative to the box's top-left corner, and can useparentWidth/parentHeightfor its size.children: a list of items, including other groups.repeat(optional):{ "rows": ..., "columns": ... }, either or both. A number repeats that many times."down"(rows) or"infinite"(rows or columns) repeats until the page, or the enclosing group, is filled. Each row steps down by the box's height; each column steps right by its width.
path draws lines and shapes.
data: a flat list of commands and coordinates, like SVG path data split into list entries:"M", x, y(move),"L", x, y(line),"C", x1, y1, x2, y2, x, y(curve), and"Z"(close the shape). Coordinates may be expressions.strokeWidth: line thickness. Leaving it out gives a very fine line;1and2are typical.strokeColorandfillColor:"#RRGGBB". Colors show in color on color devices and as gray on the reMarkable 2. A closed path with afillColordraws a filled shape.
text draws a single line of text.
text: the string.fontSize: size in page pixels (reMarkable's own templates use 22).boldanditalic: optionaltrue.position:{ "x", "y" }, relative to the enclosing group.yis the text baseline.- In
position.x,textWidthis the width of the text itself, which is how you align it:"parentWidth - textWidth"right-aligns,"(parentWidth - textWidth) / 2"centers.
Tips
- Go heavier than looks right on a monitor. E-ink washes out thin lines and light grays. Use
strokeWidth2 for anything that should stand out, and dark colors. - Rotation is not supported. Draw angled lines with explicit coordinates.
- Supporting the Paper Pro Move. Its page is much narrower. Either build everything from
templateWidth, or define a constant such as{ "compact": "templateWidth < 1000 ? 1 : 0" }and usevisibleto switch between two layouts. - Draw a checkbox as a closed path:
["M",0,0,"L",24,0,"L",24,24,"L",0,24,"Z"].
Complete example: a lined notes page
A bold heading, a date label, a heavy rule under them, and ruled lines to the bottom of the page. It adapts to each device's page width.
{
"formatVersion": 1,
"name": "Lined Notes",
"author": "Example Co.",
"templateVersion": "1.0.0",
"orientation": "portrait",
"categories": ["All"],
"labels": [],
"iconData": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNTAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSIwIDAgMTUwIDIwMCI+PHJlY3QgeD0iMSIgeT0iMSIgd2lkdGg9IjE0OCIgaGVpZ2h0PSIxOTgiIGZpbGw9IndoaXRlIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNMjAgMzBIOTBNMjAgNTBIMTMwTTIwIDcwSDEzME0yMCA5MEgxMzBNMjAgMTEwSDEzME0yMCAxMzBIMTMwTTIwIDE1MEgxMzBNMjAgMTcwSDEzMCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIzIi8+PC9zdmc+Cg==",
"constants": [
{ "margin": 100 },
{ "lineSpacing": 64 },
{ "contentWidth": "templateWidth - margin * 2" }
],
"items": [
{
"id": "title",
"type": "text",
"text": "Notes",
"fontSize": 22,
"bold": true,
"position": { "x": "margin", "y": 150 }
},
{
"id": "date",
"type": "text",
"text": "Date:",
"fontSize": 22,
"position": { "x": "templateWidth - margin - 240", "y": 150 }
},
{
"id": "header-rule",
"type": "path",
"strokeWidth": 2,
"data": ["M", "margin", 180, "L", "templateWidth - margin", 180]
},
{
"id": "lines",
"type": "group",
"boundingBox": {
"x": "margin",
"y": "180 + lineSpacing",
"width": "contentWidth",
"height": "lineSpacing"
},
"repeat": { "rows": "down" },
"children": [
{
"type": "path",
"strokeWidth": 1,
"data": ["M", 0, 0, "L", "parentWidth", 0]
}
]
}
]
}
manifest.json
manifest.json is optional, and reading it never fails an upload. If it is missing, is not valid JSON, or lacks a field, that field is simply treated as absent. Unknown fields are ignored, so tools may add their own.
| Field | Type | Meaning |
|---|---|---|
name |
string | The name shown in the tablet's template picker. Overrides template.json's name. |
packageId |
string | A stable identifier for this template across revisions. See "Updating a template" below. |
packageVersion |
number | The package format version. Write 1. Currently informational. |
Display name
The name shown on the tablet is the first non-blank value of, in order: manifest.json name, template.json name, the uploaded file's name without its extension, and finally Custom Template. Leading and trailing whitespace is trimmed.
Updating a template (packageId)
Without a packageId, every upload creates a new template, so uploading a revised file puts a second entry with the same name in the picker.
With a packageId, the template's identity on the reMarkable account is derived from it, so uploading a later revision with the same packageId replaces the earlier one in place.
- The value is trimmed and lowercased before use, so
" Acme-Dot-Grid "and"acme-dot-grid"identify the same package. - Choose something globally unique and permanent, such as a reverse-domain name:
com.example.dot-grid. Two different templates that share apackageIdwill overwrite each other. - An empty or non-string value is treated as absent.
Example
Archive layout:
dot-grid.rmtemplate
template.json
manifest.json
manifest.json:
{
"packageVersion": 1,
"packageId": "com.example.dot-grid",
"name": "Dot Grid"
}
Building the archive from a terminal, from inside the folder that holds the two files (so they land at the archive root):
zip -X dot-grid.rmtemplate template.json manifest.json
What happens on upload
Klass-RM Uploader creates a template document on your reMarkable account containing the package's template.json and the display name. The tablet downloads it on its next sync. For it to be selectable, the account needs reMarkable Connect with Methods, and the tablet needs reMarkable software 3.17 or newer. See Custom Templates for details.