---
title: "Importer (asset type)"
description: "An importer is a Luau function that converts a source file (GLB, PNG, CSV, custom binary) into one or more derived assets registered in the engine. Importers extend the engine's file-format knowledge…"
section: "Types"
slug: "types-importer"
canonical: "https://origozero.ai/docs/types-importer"
updated: "2026-08-22T07:41:56.001802987+00:00"
tags: ["asset-type", "reference"]
---

# Importer (asset type)

## When to use one

- You're bringing a new file format into the engine and want to
  derive engine-native assets from it (meshes, textures, animations,
  data tables).
- You want one VFS write to fan out into multiple derived assets
  (e.g. a GLB write derives mesh, texture, and animation assets).
- You want format ingestion to be idempotent + hot-reloadable —
  importers re-run on source-file changes.

If you only need to consume an existing engine asset type, you don't
need an importer; just resolve it via `asset.resolve`. If you're
building an authoring tool that emits assets, that's a `.tool`, not
an importer.

## Where it lives

- Source: `/zero/source/.../<name>.importer/`
- Identity: `<name>` (the `.importer` suffix strips).
- Folder shape:
  - `init.luau` (or `init.lua`) — exports `canImport` and `import`.
    **Required.**
  - `README.md` — instance documentation. **Required.**
  - `.metadata` — agent-editable tags + free-form fields. **Required.**

## How to create one

```luau
asset.create("importer", "<name>")
-- Creates: /zero/source/<name>.importer/
--   init.luau   (canImport / import stubs)
--   README.md   (instance README template)

-- `folder` places it in a subfolder of /source instead of the root:
asset.create("importer", "<name>", { folder = "importers" })
```

## How it operates

1. **Registration.** Writing `init.luau` into a `.importer/` folder
   indexes the importer with the importer registry.
2. **Dispatch.** When a file is written to VFS, the engine asks every
   registered importer `canImport(path, bytes)` (cheap predicate).
   The first importer that accepts dispatches to its `import`
   function.
3. **Derivation.** `import(path, bytes)` returns a list of
   `{ path, kind }` pairs naming the assets to derive. The engine
   instantiates each derived asset against its registered category.
4. **Idempotency.** `import` MUST be deterministic — importing the
   same source bytes twice produces identical derived assets. The
   importer pipeline uses content-hashing to short-circuit re-imports
   when bytes haven't changed.
5. **Hot reload.** Editing the importer reloads it; subsequent writes
   use the new logic. Already-imported assets stay imported with the
   old derivation until their source is imported again — writing the
   source path re-dispatches the importers, and
   `tools.use("importers", "run", <path>)` re-imports a target, many
   targets, or a whole folder on demand.

## Discovery

- `asset.list("importer")` — every registered importer.
- `asset.inspect("<name>")` — claimed extensions, derivation
  conventions, source, this type README.
- `cat /zero/source/<name>.importer` — same summary.
- `tools.use("importers", "list")` — every registered importer, with
  its name and identity.
- `tools.use("importers", "explain", <path>)` — which importers claim
  a given source and which gate would block the import.

## Authoring conventions

- Keep `canImport` cheap — it's called for every VFS write. Match on
  extension or a quick header probe; defer real parsing to
  `import`.
- Name derived assets predictably (e.g.
  `imported/<source-stem>/<asset-name>`). Predictable paths let
  callers reference derived assets directly.
- Make `import` pure relative to its inputs. Don't read mutable
  globals or side-channel state.
- Log derivations via `print` / the script log so authors can see
  what got produced from each source.

## Common pitfalls

- **Expensive `canImport`.** It runs on every VFS write; an
  expensive predicate slows down the entire authoring loop.
- **Non-idempotent `import`.** Re-importing produces different
  output → content hashing falsely caches stale results. Use only
  deterministic transforms.
- **Conflicting importers.** Two importers claiming the same
  extension causes ambiguous dispatch. First-registered wins, but
  this is fragile — coordinate ownership of file extensions.
- **Derived-asset suffixes.** Make sure derived paths end in the
  right `.<category>` suffix; otherwise they won't register as
  recognized assets.

## Related types

- `.module` — for plain ingestion helpers that don't need the
  importer dispatch path.
- `.tool` — for one-off conversions an agent invokes manually.
- `.service` — if your importer needs persistent state (e.g. a
  cache); the service owns it, the importer reads from it.
