Write a custom spells file
The form is fine for a few spells. For many, write a file and import it in Settings, Backup tab: by hand, with a script or with an AI assistant. This page has everything the file needs.
What the file is
A JSON file in the Locanda format with your spells in customSpells, no spellbooks and no settings: the same kind of file that “Export custom spells” saves.
It holds at most 1,000 spells and 5 MB.
Import is all or nothing: if one spell breaks a rule, nothing is added, and the import lists what to fix and where. For a spell whose id is already on this device, you choose whether to replace it or keep both.
An example
A whole file with one cantrip, written in English and Italian. Copy it as a starting point.
{
"format": "locanda",
"schemaVersion": 1,
"exportedAt": "2026-10-25T12:00:00Z",
"appVersion": "0.0.0",
"dataVersion": "custom",
"spellbooks": [],
"customSpells": [
{
"id": "custom:6c49a5ff-2c31-4316-b0ce-0ebd82a9d04d",
"source": "custom",
"level": 0,
"school": "illusion",
"classes": [
"bard",
"sorcerer",
"wizard"
],
"castingTime": {
"unit": "action",
"amount": 1
},
"ritual": false,
"range": {
"type": "ranged",
"distance": 30,
"unit": "feet",
"area": null
},
"components": {
"v": false,
"s": true,
"m": null
},
"duration": {
"type": "timed",
"amount": 1,
"unit": "hour"
},
"concentration": false,
"damageTypes": [],
"savingThrows": [],
"text": {
"en": {
"name": "Lantern Moth",
"castingCondition": null,
"material": null,
"description": "A moth of soft light appears at a point you can see within range. It sheds dim light in a 10-foot radius and follows the path you trace with one finger.",
"higherLevels": null,
"cantripUpgrade": "You create one more moth when you reach levels 5, 11 and 17."
},
"it": {
"name": "Falena lanterna",
"castingCondition": null,
"material": null,
"description": "Una falena di luce tenue appare in un punto entro gittata che puoi vedere. Emana luce fioca in un raggio di 3 metri e segue il percorso che tracci con un dito.",
"higherLevels": null,
"cantripUpgrade": "Crei una falena in più quando raggiungi il 5°, l'11° e il 17° livello."
}
},
"reference": null,
"tags": [
"Homebrew"
],
"createdAt": "2026-10-25T12:00:00Z",
"updatedAt": "2026-10-25T12:00:00Z"
}
]
}
The fields
Write every field in the tables, in any order. Where a field has nothing to say, write null or [] as the table says; never leave it out. Fields that are not in the tables are refused. Line breaks and tabs are allowed only in the Markdown fields.
The file
| Field | What it holds |
|---|---|
format | Always "locanda". |
schemaVersion | Always 1. |
exportedAt | When the file was made: a date and time in RFC 3339, such as 2026-10-25T12:00:00Z. |
appVersion | The version of the app that wrote the file, such as 1.0.0. A file you write can use 0.0.0. |
dataVersion | Lowercase letters, digits, dots and hyphens, such as custom. |
spellbooks | Always []: a custom spells file has no spellbooks. |
customSpells | The spells: from 1 to 1,000, each with its own id. |
Each spell
| Field | What it holds |
|---|---|
id | custom: followed by a new random UUID (version 4, lowercase), such as custom:6c49a5ff-2c31-4316-b0ce-0ebd82a9d04d. Make a new one for every spell: the same id means the same spell. |
source | Always "custom". |
level | A whole number from 0 (a cantrip) to 9. |
school | One of the schools below. |
classes | A list of the classes below, at least one, each once, such as ["wizard"]. |
castingTime. | One of the casting time units below. |
castingTime. | How many actions, minutes or hours: a whole number from 1 to 1,000. Use 1 for an action, a bonus action or a reaction. |
ritual | true or false: it can be cast as a ritual. |
range. | One of the range types below. |
range. | For a ranged spell, the distance: a number above 0, up to 1,000,000. For every other range type, null. |
range. | For a ranged spell, feet or miles; for every other range type, null. Distances are in feet, never metres: the app converts them. |
range. | null, or the area of effect: an object with shape (one of the area shapes below), size (a number above 0) and unit (feet or miles). |
components. | true or false: it has a verbal component. |
components. | true or false: it has a somatic component. |
components. | null with no material component; otherwise an object with costGp (its cost in gold pieces, 0 to 1,000,000, or null) and consumed (true or false). The material’s words go in text. |
duration. | One of the duration types below. |
duration. | For a timed duration, a whole number from 1 to 1,000; otherwise null. |
duration. | For a timed duration, one of the duration units below; otherwise null. |
concentration | true or false: it needs concentration. |
damageTypes | A list of the damage types below, each once; [] for none. |
savingThrows | A list of the abilities below that the spell calls for saving throws with, each once; [] for none. |
text | The spell’s words by language: an object with en, it or both, each holding the fields below. Where a language is missing, the app shows the other. At least one language needs a name. |
text. | The spell’s name: one line of 1 to 100 characters, or null. |
text. | When a reaction is taken, or another condition on the casting time: one line of 1 to 500 characters, or null. |
text. | The material component: one line of 1 to 500 characters, or null. Needed in at least one language when components.m is an object, and only then. |
text. | The description in Markdown, up to 20,000 characters. Write "" to keep only the reference. |
text. | For a spell of level 1 or more, what a higher-level slot does, in Markdown (1 to 20,000 characters), or null. Always null for a cantrip. |
text. | For a cantrip, how it grows with the character’s level, in Markdown (1 to 20,000 characters), or null. Always null for a spell of level 1 or more. |
reference | Where the full text is, such as a book and page: one line of 1 to 100 characters, or null. |
tags | Your own labels, such as a campaign: at most 10, each of 1 to 30 characters with no spaces at either end, no two alike once case and accents are ignored; [] for none. Tags are the same in every language. |
createdAt | When the spell was written: a date and time in RFC 3339, such as 2026-10-25T12:00:00Z. |
updatedAt | When it last changed, in the same form: the same as createdAt for a new spell. |
Allowed values
These fields take only the keys below, written exactly as here, in English whatever the language of the text. The words after a key are how Locanda shows it.
Schools
Used in: school
abjuration– Abjurationconjuration– Conjurationdivination– Divinationenchantment– Enchantmentevocation– Evocationillusion– Illusionnecromancy– Necromancytransmutation– Transmutation
Classes
Used in: classes
bard– Bardcleric– Clericdruid– Druidpaladin– Paladinranger– Rangersorcerer– Sorcererwarlock– Warlockwizard– Wizard
Casting time units
Used in: castingTime.unit
actionbonus-actionreactionminutehour
Range types
Used in: range.type
self– Selftouch– Touchranged– Rangedsight– Sightunlimited– Unlimitedspecial– Special
Distance units
Used in: range.unit, range.area.unit
feetmiles
Area shapes
Used in: range.area.shape
cone– Conecube– Cubecylinder– Cylinderemanation– Emanationline– Linesphere– Sphere
Duration types
Used in: duration.type
instantaneous– Instantaneoustimed– A set timeuntil-dispelled– Until dispelleduntil-dispelled-or-triggered– Until dispelled or triggeredspecial– Special
Duration units
Used in: duration.unit
round– Roundsminute– Minuteshour– Hoursday– Days
Damage types
Used in: damageTypes
acid– Acidbludgeoning– Bludgeoningcold– Coldfire– Fireforce– Forcelightning– Lightningnecrotic– Necroticpiercing– Piercingpoison– Poisonpsychic– Psychicradiant– Radiantslashing– Slashingthunder– Thunder
Saving throw abilities
Used in: savingThrows
str– Strengthdex– Dexteritycon– Constitutionint– Intelligencewis– Wisdomcha– Charisma
Text languages
Used in: text
en– Englishit– Italiano
JSON Schemas
To check a file before you import it, or to give a script or an AI assistant the exact rules, download the three JSON Schemas (draft 2020-12):
- Export file schema (
urn:locanda:schema:export:1) - Custom spell schema (
urn:locanda:schema:custom-spell) - Spell schema (
urn:locanda:schema:spell)
Load all three into the same validator, then validate the file against urn:locanda:schema:export:1: the schemas refer to each other by these urn: ids, not by web address. The format keywords are for information only, as a pattern checks the same fields. With Ajv, for example:
import Ajv2020 from 'ajv/dist/2020.js';
const ajv = new Ajv2020({ allErrors: true, validateFormats: false });
ajv.addSchema([exportSchema, customSpellSchema, spellSchema]);
const valid = ajv.validate('urn:locanda:schema:export:1', file);
console.log(valid ? 'Valid' : ajv.errors);
A few rules are checked only by Locanda when it imports: a name in at least one language; higher-level text only on spells of level 1 or more, and cantrip text only on cantrips; material text only with a material component; real calendar dates; no two tags alike; and each id only once in the file.
A prompt for an AI assistant
To turn your own notes into a file, give an AI assistant this prompt with the three schemas and the example file attached, then paste your notes after it. Check what it returns: assistants make mistakes.
I will paste notes about spells I wrote for my own game. Turn them into one Locanda custom spells file that follows the attached JSON Schemas (export.schema.json, custom-spell.schema.json and spell.schema.json) and looks like the attached example file.
Rules:
- The file is one JSON object with "format": "locanda", "schemaVersion": 1, "exportedAt" set to the current time in RFC 3339 (UTC), "appVersion": "0.0.0", "dataVersion": "custom", "spellbooks": [] and "customSpells" with one object per spell.
- Give every spell a new random id: "custom:" followed by a lowercase version 4 UUID.
- Use only the English enum keys the schemas allow (schools, classes, units, range types, area shapes, damage types, abilities), even if my notes are in another language.
- Put the words of each spell under "text", in the language of my notes ("en" for English, "it" for Italian). Write null for what my notes do not say. Write descriptions in Markdown, without HTML.
- Give distances in feet.
- Set "createdAt" and "updatedAt" to the same time as "exportedAt", "tags" to [] and "reference" to null, unless my notes give them.
- Do not invent spells, rules or text that are not in my notes. If something the schemas require is missing or unclear, ask me instead of guessing.
Reply with the JSON file only.
My notes:Before you import
- Every spell has a new id of its own (
custom:and a fresh UUID), not one copied from the example. - Every value in the fields above is one of the allowed keys, in English, even when the text is in Italian.
- The file passes a validator with the schemas above. Or simply import it: Locanda checks the whole file first, and if anything is wrong it adds nothing and says what to fix.
Whose spells
Put in the file only spells you wrote yourself or have the right to use, such as your group’s homebrew. Locanda does not check where the text comes from, and the file stays on your device unless you share it.