Dynamic content with templates
The Template column in Custom data definition table lets you transform a raw data value into a formatted display string before rendering. Templates use the Handlebars syntax: {{variableName}}.
Available variables
| Variable | Content |
|---|---|
{{value}} | The raw value of the current field (as a string) |
{{taxon.name}} | The leaf taxon name (e.g. Litoria aurea) |
{{taxon.authority}} | The taxon authority (e.g. (Lesson, 1829)) |
{{taxon.fullName}} | Name + authority combined (e.g. Litoria aurea (Lesson, 1829)) |
{{taxon.ranks.[rank name].name}} | The chosen rank's name (using ranks from Taxa definition), e.g. {{taxon.ranks.order.name}} → Anura |
{{taxon.ranks.[rank name].authority}} | The chosen rank's taxonomic authority |
{{taxon.ranks.[rank name].fullName}} | The chosen rank's name + authority combined |
{{data.*}} | Any other data field from the same taxon row, accessed by data path |
{{lat}} | (geopoint only) The parsed latitude as a decimal number |
{{long}} | (geopoint only) The parsed longitude as a decimal number |
Templates and the rendering pipeline
The template is applied to the data value before the type-specific renderer runs. Read Data types to see how templates work with each individual data type.
Templates support multilingual variants: Template:en, Template:fr, etc.
{{img}} - images and maps with thumbnail support
The img helper is the recommended way to reference image and map (do not confuse with mapregions) data types. It adds proper thumbnail support on top of what a plain path template can do.
How the full path is assembled
All image and map files must live inside the usercontent/ folder. Every path a template produces is resolved relative to that root - the app always prefixes it automatically, so you never write usercontent/ in the template itself.
Within that, a template using {{img}} has three distinct parts:
images/ {{img (thumb "*_640") (full "*_2400")}} .jpg
│ │ │
│ └─ stem: resolved by {{img}} └─ extension: static
└─ prefix: staticThe prefix (folder path) and extension are static text you write once in the template cell. {{img}} is responsible only for the stem - the filename portion between them. For the example above, the files the app fetches are:
| Context | Full path on disk |
|---|---|
| Thumbnail | usercontent/images/banksia_serrata_640.jpg |
| Full-size | usercontent/images/banksia_serrata_2400.jpg |
This separation gives you full control over your folder layout and file format through the static parts of the template, while {{img}} takes care of switching between the thumbnail and full-size stems automatically.
A plain path template like specimenScans/{{value}}.jpg works on the same principle - the folder and extension are static, {{value}} provides the stem - but it cannot distinguish thumbnail from full-size, so the app treats both as the same file. {{img}} lifts that constraint.
Why thumbnail support matters
Without declared variants, the app has no way to distinguish between the full-size image and a smaller preview, so it treats both contexts as the same file. This means:
- Offline precaching downloads the full-size file. If your images are high-resolution specimen scans or habitat photographs, precaching them all may consume hundreds of megabytes and make the app slow in use or to prepare offline.
- No large-file workflow. There is no way to serve a very large file - a detailed scan, a high-resolution illustration - that stays off-device until the user explicitly opens it.
The img helper solves both problems. You declare which file is the thumbnail and which is the full-size. The app then precaches only the thumbnails - small enough to make the app fast and useful even on a slow connection or completely offline - while still serving the full-size image on demand when a user opens it while online. Once fetched, the full-size is cached and available offline thereafter.
Precaching and offline use
Set your thumbnail dimensions to something relatively small but still useful when viewed - 640 or 800 px on the long edge is a good target with proper compression. This keeps precaching fast. The full-size file has no size restriction; it is not fetched until the user explicitly opens the image.
How thumbnail files are produced
The img helper does not generate thumbnails - it resolves which filename to use at display time. You produce the thumbnail files yourself, once, with any batch image tool (ImageMagick, XnConvert, Lightroom export presets, or similar). The naming convention is entirely yours; you define it in the template. A common approach is a size suffix:
| File on disk | Role |
|---|---|
usercontent/images/hakea_prostrata.jpg | Base image (or full-size when no full pattern is declared) |
usercontent/images/hakea_prostrata_640.jpg | Thumbnail (e.g. 640 px) - produced by your batch tool |
usercontent/images/hakea_prostrata_2400.jpg | Large scan (e.g. 2400 px) - produced by your batch tool |
Once the files are in place and the template is set, a single template definition covers every taxon in your dataset. The data author enters only the slug (hakea_prostrata) in their data cell; the app builds the right path for each context automatically.
Syntax
{{img}}
{{img value}}
{{img (thumb "*_640")}}
{{img (full "*_2400")}}
{{img (thumb "*_640") (full "*_2400")}}
{{img value (thumb "*_640") (full "*_2400")}}
{{! _640 suffix for thumbnails and _2400 suffix for full size }}
{{img (thumb "sm_*")}}
{{img (thumb "sm_*") (full "lg_*")}}
{{! sm_ prefix for thumbnails and lg_ prefix for full size }}
{{img (thumb data.thumbSlug)}}
{{! thumbnail stem taken from column thumbSlug }}
{{img (full data.fullSlug)}}
{{! full-size stem taken from column fullSlug }}
{{img (thumb data.thumbSlug) (full data.fullSlug)}}
{{img (thumb "*_640") (full data.fullSlug)}}The base value is the stem shared by both variants before any pattern is applied. When omitted, it comes from the current cell's value. When provided explicitly, it is an unquoted data path (e.g. data.otherCol).
The file extension is always written outside {{img}} as static text in the template string - never inside the helper:
images/{{img (thumb "*_640")}}.jpg ← correct images/{{img (thumb "*_640.jpg")}}
← do not do thisThe * placeholder
The key concept
* is a placeholder for the base value inside a pattern. The helper substitutes the actual base value in place of * at display time.
pattern "*_640" + base value "cerbera" → "cerbera_640"
pattern "sm_*" + base value "cerbera" → "sm_cerbera"A pattern without * is treated as a complete stem on its own - the base value is ignored entirely and the pattern string (or the resolved value of a data column) is used as-is. This is how you point a variant at an independent filename or a separate data column.
Both (thumb …) and (full …) follow the same rules. Arguments may appear in any order inside {{img}}. When no variant subexpressions are given at all, {{img}} behaves identically to {{value}}. Plain-path templates require no migration.
When only (full) is declared and no (thumb) is present, the thumbnail falls back to the full variant rather than the bare base value, ensuring the template is always safe to use.
Examples
Each example shows:
nl_contentCustom Data Definition - the Custom data definition Column name and the Template cell you fill in for that column's row.- Data sheet - what the data author actually types into the data column for each taxon.
- Files on disk - which files must exist (paths are relative to
usercontent/). - App resolves → - what the app constructs for each display context.
No variants - plain path
nl_content → Column name | photo |
nl_content → Template | images/{{img}}.jpg |
Data sheet → photo column | osmoxylon |
| Files on disk | images/osmoxylon.jpg |
| → Thumbnail | usercontent/images/osmoxylon.jpg |
| → Full-size | usercontent/images/osmoxylon.jpg |
No (thumb) or (full) declared. Both contexts resolve to the same file. Equivalent to the plain images/{{value}}.jpg approach.
Suffix thumbnail, base file as full-size
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb "*_640")}}.jpg |
Data sheet → photo column | cerbera |
| Files on disk | images/cerbera.jpg · images/cerbera_640.jpg |
| → Thumbnail | usercontent/images/cerbera_640.jpg |
| → Full-size | usercontent/images/cerbera.jpg |
* is replaced by the base value, then _640 is appended. No (full) pattern is declared, so the full-size falls back to the bare base file.
Suffix thumbnail and suffix full-size (most common)
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb "*_640") (full "*_2400")}}.jpg |
Data sheet → photo column | agastarche |
| Files on disk | images/agastarche_640.jpg · images/agastarche_2400.jpg |
| → Thumbnail | usercontent/images/agastarche_640.jpg |
| → Full-size | usercontent/images/agastarche_2400.jpg |
Both variants are derived from the same base value by the same suffix rule. The base file (agastarche.jpg) does not need to exist on disk - only the two sized variants do.
Full-size only declared - thumbnail falls back to full
nl_content → Column name | photo |
nl_content → Template | images/{{img (full "*_2400")}}.jpg |
Data sheet → photo column | grevillea_robusta |
| Files on disk | images/grevillea_robusta_2400.jpg |
| → Thumbnail | usercontent/images/grevillea_robusta_2400.jpg |
| → Full-size | usercontent/images/grevillea_robusta_2400.jpg |
When only (full) is present, the thumbnail falls back to the full variant. Only one file is needed on disk. Use this when you want to declare the full-size naming explicitly but have no separate thumbnail export.
Prefix thumbnail, base file as full-size
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb "sm_*")}}.jpg |
Data sheet → photo column | hakea |
| Files on disk | images/hakea.jpg · images/sm_hakea.jpg |
| → Thumbnail | usercontent/images/sm_hakea.jpg |
| → Full-size | usercontent/images/hakea.jpg |
sm_ is prepended to the base value. Use this when your batch export tool names files by role prefix rather than by size suffix.
Prefix thumbnail and prefix full-size
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb "sm_*") (full "lg_*")}}.jpg |
Data sheet → photo column | melaleuca |
| Files on disk | images/sm_melaleuca.jpg · images/lg_melaleuca.jpg |
| → Thumbnail | usercontent/images/sm_melaleuca.jpg |
| → Full-size | usercontent/images/lg_melaleuca.jpg |
Both variants use a prefix pattern. The base file does not need to exist.
Mixed - suffix thumbnail, prefix full-size
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb "*_640") (full "hires_*")}}.jpg |
Data sheet → photo column | eucalyptus_regnans |
| Files on disk | images/eucalyptus_regnans_640.jpg · images/hires_eucalyptus_regnans.jpg |
| → Thumbnail | usercontent/images/eucalyptus_regnans_640.jpg |
| → Full-size | usercontent/images/hires_eucalyptus_regnans.jpg |
(thumb) and (full) are fully independent - each can use any pattern. Mix suffix and prefix freely to match whatever naming convention your tools produce.
Explicit base value from a different column
nl_content → Column name | photo |
nl_content → Template | images/{{img data.photo (thumb "*_640") (full "*_2400")}}.jpg |
Data sheet → stem column | banksia_serrata |
Data sheet → photo column | specimen_0187 |
| Files on disk | images/specimen_0187_640.jpg · images/specimen_0187_2400.jpg |
| → Thumbnail | usercontent/images/specimen_0187_640.jpg |
| → Full-size | usercontent/images/specimen_0187_2400.jpg |
Passing data.photo as the first argument overrides the implicit base - the patterns are applied to specimen_0187 instead of the current column's value. Use this when the image identifier lives in a different column from the one the template is attached to.
Data column overrides thumbnail stem - no *
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb data.thumbSlug) (full "*_2400")}}.jpg |
Data sheet → photo column | acacia_pycnantha |
Data sheet → thumbSlug column | acacia_pycnantha_thumb |
| Files on disk | images/acacia_pycnantha_thumb.jpg · images/acacia_pycnantha_2400.jpg |
| → Thumbnail | usercontent/images/acacia_pycnantha_thumb.jpg |
| → Full-size | usercontent/images/acacia_pycnantha_2400.jpg |
data.thumbSlug contains no *, so its resolved value is used as the complete thumbnail stem - the base value (acacia_pycnantha) plays no part in building the thumbnail path. The full-size still applies the * pattern against the base. Use this when thumbnail filenames follow no predictable pattern and must be tracked individually per taxon.
Both variants from independent data columns - no * in either
nl_content → Column name | photo |
nl_content → Template | images/{{img (thumb data.thumbSlug) (full data.fullSlug)}}.jpg |
Data sheet → thumbSlug column | litoria_aurea_web |
Data sheet → fullSlug column | litoria_aurea_hires |
| Files on disk | images/litoria_aurea_web.jpg · images/litoria_aurea_hires.jpg |
| → Thumbnail | usercontent/images/litoria_aurea_web.jpg |
| → Full-size | usercontent/images/litoria_aurea_hires.jpg |
Neither pattern contains *. Both stems are taken entirely from their respective data columns; the base value is not used at all. Use this when thumbnail and full-size filenames share no common stem and must be tracked separately per taxon.
Variants in separate subfolders via data columns
nl_content → Column name | photo |
nl_content → Template | {{img (thumb data.thumbPath) (full data.fullPath)}}.jpg |
Data sheet → thumbPath column | thumbs/grevillea_robusta |
Data sheet → fullPath column | scans/grevillea_robusta |
| Files on disk | thumbs/grevillea_robusta.jpg · scans/grevillea_robusta.jpg |
| → Thumbnail | usercontent/thumbs/grevillea_robusta.jpg |
| → Full-size | usercontent/scans/grevillea_robusta.jpg |
The data columns carry the subfolder as part of the value. There is no static prefix in the template - the entire path before the extension comes from the data. This lets you organise thumbnail and full-size files into completely separate directory trees, with each taxon's folders encoded in the data rather than hard-coded in the template.
Quick reference
All resolved paths are relative to usercontent/. The template prefix and extension columns show the static parts surrounding {{img …}} in the template cell.
| Template prefix | {{img …}} arguments | Extension | Data column(s) | → Full-size stem | → Thumbnail stem |
|---|---|---|---|---|---|
images/ | (none) | .jpg | photo = osmoxylon | osmoxylon | osmoxylon |
images/ | (thumb "*_640") | .jpg | photo = cerbera | cerbera | cerbera_640 |
images/ | (full "*_2400") | .jpg | photo = grevillea_robusta | grevillea_robusta_2400 | grevillea_robusta_2400 † |
images/ | (thumb "*_640") (full "*_2400") | .jpg | photo = agastarche | agastarche_2400 | agastarche_640 |
images/ | (thumb "sm_*") (full "lg_*") | .jpg | photo = melaleuca | lg_melaleuca | sm_melaleuca |
images/ | (thumb "*_640") (full "hires_*") | .jpg | photo = eucalyptus_regnans | hires_eucalyptus_regnans | eucalyptus_regnans_640 |
images/ | data.photo (thumb "*_640") (full "*_2400") | .jpg | photo = specimen_0187 | specimen_0187_2400 | specimen_0187_640 |
images/ | (thumb data.thumbSlug) (full "*_2400") | .jpg | thumbSlug = acacia_pycnantha_thumb, photo = acacia_pycnantha | acacia_pycnantha_2400 | acacia_pycnantha_thumb |
images/ | (thumb data.thumbSlug) (full data.fullSlug) | .jpg | thumbSlug = litoria_aurea_web, fullSlug = litoria_aurea_hires | litoria_aurea_hires | litoria_aurea_web |
| (none) | (thumb data.thumbPath) (full data.fullPath) | .jpg | thumbPath = thumbs/grevillea_robusta, fullPath = scans/grevillea_robusta | scans/grevillea_robusta | thumbs/grevillea_robusta |
† When only (full) is declared and no (thumb) is present, the thumbnail falls back to the full variant.
{{unit}} - automatic unit scaling
The unit helper takes a numeric value and a unit of measurement, then automatically scales the output to the most readable unit in the same category. This is strongly recommended for all numeric measurement fields - it lets you store data in a consistent base unit while displaying it in the most human-readable form. For example, in a mammal dataset your cell data could be entered as a typical or per-sex interval in grams, but would get automatically scaled, remaining grams for mice but automatically converted to tons for elephants.
Best practice
Store bare numbers in your data cells (e.g. 5) and use the template to format them (e.g. {{unit "m"}}). Do not enter 5 m or 8 kg in cells - that turns the value into text and disables numeric filtering.
Syntax
{{unit "m"}}
{{! reads value from current context }}
{{unit data.myField "m"}}
{{! explicit field reference }}
{{unit "kg" "exact"}}
{{! current context value, no unit scaling }}
{{unit data.myField "kg" "exact"}}
{{! explicit field, exact mode }}Supported units
| Category | Input units |
|---|---|
| Length | um, mm, cm, m, km |
| Time | ms, s, min, h, d, y |
| Weight | mg, g, kg, t |
| Area | um2, mm2, cm2, m2, km2 |
| Volume (cubic) | um3, mm3, cm3, m3, km3 |
| Volume (liquid) | ml, l |
Combined units using / are supported for rates and densities - e.g. km/h, m3/s, kg/m2. Only the numerator is scaled; the denominator remains as specified.
Scaling examples
| Template | Raw cell value | Output |
|---|---|---|
{{unit "m"}} | 0.05 | 5 cm |
{{unit "m"}} | 1500 | 1.5 km |
{{unit "g"}} | 2500 | 2.5 kg |
{{unit "s"}} | 7200 | 2 h |
{{unit "cm2"}} | 10000 | 1 m² |
{{unit "m/s"}} | 2500 | 2.5 km/s |
Formatting rules
- Numbers are rounded to a maximum of 2 decimal places with trailing zeros removed.
- A non-breaking space is placed between the number and the unit symbol.
- Area and volume units render with proper superscripts:
cm²,m³.
Interval values
When a field contains two numbers (an interval), the helper is applied to each endpoint independently:
{{unit "m"}}
{{! interval [0.5, 1.2] → "50 cm – 1.2 m" }}
{{unit "m"}}
{{! interval [1.1, 1.5] → "1.1 m – 1.5 m" }}
{{unit "m"}}
{{! interval [3, 3] → "3 m" }}If both endpoints resolve to the same number and unit, a single value is shown.
Exact mode
Pass "exact" as an additional argument to skip all unit scaling. Useful when you want to display data in the original unit regardless of magnitude, or when using a custom unit string not in the built-in dictionary:
{{unit "kg" "exact"}}
{{! 0.005 → "0.005 kg" (no conversion to g) }}
{{unit "eggs" "exact"}}
{{! custom unit string }}Render multiple columns in one place
Suppose you have a colName collum with collector's name and colNum column with collector number and you want to render them together. You can set the colNum hidden to data in Hidden, it will still keep it filtrable, just won't show up in the taxon card. Then you use this template on colName:
{{value}} {{colNum}}The value bears the current value of colName and the whole will render e.g. John Smith 457
Select a different map per taxon:
maps/{{data.region}}.svgIf a taxon has region = africa, the SVG path becomes maps/africa.svg. An advanced use-case of Distribution maps with mapregions to show a different region-focused map in an otherwise global dataset.
Conditional content with Handlebars helpers:
{{#if value}}Present in {{value}} region{{/if}}{{value}} {{#ifeq value "1"}}population{{else}}populations{{/ifeq}}