Marko

marko.json & marko-tag.json

Marko supports configuration files for validation, enabling experimental features, and custom paths for component files.

These configuration files are automatically found with the same discovery mechanism as custom tags.

There are 2 types of configuration files:

  1. marko.json describes an entire suite of components.
  2. marko-tag.json describes a single component.

Single component definition

marko-tag.json configures a single component. It’s automatically discovered if placed inside a tag directory.

Options

{
  "html": true, // Treat as a native HTML tag, not a custom tag.
  "htmlType": "svg", // Optimizes for specific types of native tags (currently only `svg` and `html`).
  "open-tag-only": true, // Forbids passing body content to this tag.
  "featureFlags": [ "feature-a" ], // Enable beta features by passing feature flags.
  "nested-tags": { // This section configures attribute tags.
    "tab": {
      "target-property": "tabs", // Puts `<@tab>` tags into `input.tabs`.
      "is-repeated": true,  // Allow more than one nested `<@tab>`.
      "attributes": {
        // Same as the “Attributes” section below.
      }
    }
  }
}

Attributes

One commonly-used feature of this config file is compile-time checks for attributes.

{
  "attributes": {
    "heading": "string"
  }
}

The above code ensures that the heading attribute is the only attribute supplied to this tag.

The string value is used as documentation for the custom tag. It may be picked up by tooling, like Marko’s editor plugins, to provide hints to the user.

The recommended list of attribute types are as follows:

  • expression (any JavaScript expression)
  • string
  • number
  • boolean
  • regexp
  • date
  • object
  • array
  • function

You can also provide an object for an attribute definition’s value for additional options:

{
  "attributes": {
    "heading": {
      "type": "string", // Same as setting "string" above.
      "default-value": 0, // The attribute will default to this value.
      "required": true, // Error during compilation if this attribute is undefined. (Mutually exclusive with "default-value"
      "preserve-name": true, // By default component attributes are camelCased; this disables that feature.
      "remove-dashes": true, // By default native tag attributes are dash-cased; this disables that feature.

      // The following attributes do nothing, but are picked up by tooling.
      "deprecated": true,
      "description": "The component’s heading text" // Describes the attribute’s purpose.
    }
  }
}

We can also describe a pattern of attributes to match a definition:

{
    "attributes": {
        "data-*": {
          "type": "string",
          "pattern": true
        }
    }
}

In the above, all attributes prefixed with data- are configured to be a string.

Note: Future Marko versions will describe these definitions/types in the component itself, reducing the need for this configuration file.

Paths

There are several options that override the default discovery of component files, such as the template.

Typically, you should let Marko find these files automatically, but here is a reference in case you encounter these settings in the wild.

{
  "template": "./template.marko", // Custom path to the `.marko` template.
  "renderer": "./renderer.js", // Custom path to the `renderer.js` file.

  // Compiler file hooks
  "parse": "./parse.js", // Used to augment parsing.
  "migrate": "./migrate.js", // Used for migrating deprecated features.
  "transform": "./transform.js", // Used to modify the AST before generating it.
  "translate": "./translate.js" // Used to generate custom JS.
}

For more information about the compiler hooks jump over here.

Tag library definition

Along with configuring a single component, you can use a marko.json file to configure an entire library of components.

Similar to marko-tag.json, this file is discovered if placed within a tag directory. It will also be discovered at the root directory of a project, or in a node_module package.

Options

{
  "taglib-id": "my-custom-tag-library", // Names the component library, for better errors.
  "tags-dir": "./ui-modules", // What directory to crawl to autodiscover components. Default:`./components/`
  "taglib-imports": ["./some-folder/marko.json", "./other-folder/marko.json"], // Creates a _combined_ tag library by referencing others.

  "tags": { // Definitions for individial tags.
    "my-tag": {
      // Same options as “marko-tag.json”.
    }
  },

  "attributes": {
    // Defines attributes on all tags.
    // Options are the same as the “attributes” section in “marko-tag.json”.
  },

  // Compiler file hooks (run on all templates)
  "migrator": "./migrator.js", // Hooks into the migration stage for migrating deprecated features.
  "transformer": "./transformer.js", // Used to modify the AST before generating it.
  "text-transformer": "./text-transformer.js", // Used to transform all static text in the template.
}

⚠️ Note: Compiler hooks are currently undocumented: avoid using them. The compiler API is overhauled in Marko 5, and will be documented once that transition is complete.

Shorthands

Both configuration files support shorthands for defining tags and attributes. For example, take this marko.json file:

marko.json
{
  "taglib-id": "my-custom-tag-library",
  "tags": {
    "my-layout": {
      "attributes": {
        "name": "string",
        "age": "number"
      },
      "nested-tags": {
        "heading": {
          "attributes": {
            "color": "string"
          }
        },
        "body": {
          "attributes": {
            "color": "string"
          }
        }
      }
    }
  }
}

As a shorthand, anywhere tags or nested-tags is used, you can remove the outer object and wrap the individual tags in <angle-brackets>.

For attributes, you can remove the outer object and prefix the attributes with an @.

The above example using the shorthand syntax would become:

marko.json
{
  "taglib-id": "my-custom-tag-library",
  "<my-layout>": {
    "@name": "string",
    "@age": "number",
    "<heading>": {
      "@color": "string"
    },
    "<body>": {
      "@color": "string"
    }
  }
}

For nested-tags, there is also a shorthand for is-repeated (a postfix of []) and target-property (a prefix of @newName):

marko.json
{
  "<my-layout>": {
    "@sections <section>[]": {
      "@color": "string"
    }
  }
}

Is equivalent to:

marko.json
{
  "tags": {
    "my-layout": {
      "nested-tags": {
        "section": {
          "target-property": "sections",
          "is-repeated": true,
          "attributes": {
            "color": "string"
          }
        }
      }
    }
  }
}
EDIT

Contributors

Helpful? You can thank these awesome people! You can also edit this doc if you see any issues or want to improve it.

Chat in Marko's Discord Server