Views
Tags filter your content. Views configure how the filtered content renders: which sections appear, in what order, what keywords to inject, how many pages to target. See How Tailoring Works for the mental model.
Four Kinds of View
Every render uses a view. They differ in where they live and how much effort they take to create.
| Kind | Where | Nature |
|---|---|---|
| Default view | Frontmatter render fields | Base config for all renders |
| Tag view | Frontmatter tags: expanded form | Per-tag overrides, implicit for every tag |
| Custom view | .view.yaml files | Per-application config |
| Ephemeral view | CLI flags | One-off, not persisted |
Tag views and custom views are peers, not a hierarchy. --for resolves to one or the other, never both. See Cascade Order for details.
Default View
Frontmatter render fields (pages, style, vars, sections, bullet-order) are the default view. They apply to every render unless overridden by a more specific view.
---
pages: 1
sections:
hide: [publications]
style:
section-title-color: '#2563eb'
vars:
tagline: 'Full-stack engineer with 8 years of experience'
---A resume with no frontmatter still has a default view, it just uses built-in defaults for everything.
Tag Views
Every tag implicitly generates a tag view. With no configuration, the tag view inherits all defaults from the default view. You can configure the tag view using the expanded form in frontmatter:
---
tags:
frontend:
sections:
hide: [publications]
pin: [skills, projects]
pages: 1
fullstack:
extends: [frontend, backend]
sections:
pin: [work, skills]
pages: 2
---The shorthand fullstack: [frontend, backend] is sugar for fullstack: { extends: [frontend, backend] }. Use the expanded form when you need render configuration on the tag view.
Configuring a tag view doesn't change the tag. Other tags can still compose it, {.@frontend} still works in the body. The tag handles content (what to show), its tag view handles rendering (how to show it).
Custom Views
Custom views live in .view.yaml files. Top-level keys are view names:
# yaml-language-server: $schema=https://resumx.dev/schemas/view.schema.json
stripe-swe:
selects: [backend/node, distributed-systems, leadership]
sections:
hide: [publications]
pin: [skills, work]
vars:
tagline: 'Stream Processing, Event-Driven Architecture, Node.js, Kafka'
stripe-pm:
selects: [frontend, leadership]
vars:
tagline: 'Product Strategy, Cross-functional Leadership'Resumx recursively discovers all **/*.view.yaml files relative to the resume. Organize however you want:
resume.md
stripe-swe.view.yaml
views/
active/
netflix.view.yaml
archive/
old-google.view.yamlRender with --for:
resumx resume.md --for stripe-sweView resolution
--for resolves the name against both custom views and tag views. If a name matches both, Resumx raises an error. Glob patterns (--for 'stripe-*') match against all named views (tag and custom). See CLI Reference for the full list of --for patterns.
Custom View Fields
| Field | Type | Description |
|---|---|---|
selects | string[] | Content tags to include (union). |
sections | object | Section visibility and ordering (see Sections). |
pages | number | Target page count (overrides frontmatter). |
bullet-order | none | tag | Bullet ordering strategy. Default: none. |
vars | Record<string, string> | Variable values for {{ }} placeholders. |
style | Record<string, string> | Style overrides (same as frontmatter style). |
css | string | string[] | CSS file path(s) or inline CSS string(s). |
format | string | Output format (pdf, html, docx, png). |
output | string | Output path (same as frontmatter output). |
Base defaults (pages, style, bullet-order) live in frontmatter. Custom view fields are overrides.
JSON Schema
A JSON Schema is available for view files (and for frontmatter). Add a comment at the top of any .view.yaml to get validation, autocompletion, and hover docs:
# yaml-language-server: $schema=https://resumx.dev/schemas/view.schema.jsonThis works in any editor that uses the YAML Language Server (VS Code, Cursor, Neovim, etc.), no settings file required.
To enable it project-wide without adding a comment to each file, add this to .vscode/settings.json:
{
"yaml.schemas": {
"https://resumx.dev/schemas/view.schema.json": "**/*.view.yaml"
}
}Content Filtering
A custom view without selects applies no content filter. All content renders, tagged or not. This is useful when a view only needs render configuration:
one-pager:
sections:
pin: [skills, work]
pages: 1An explicit empty array (selects: []) is different: it means "select no tags," so only untagged content appears. Tagged content is stripped. Think of it like a SQL query: omitting the WHERE clause returns everything, while WHERE tag IN () matches nothing. Every tag name in selects must exist as a content tag, a composed tag, or a tag view; unknown names produce an error with a suggestion for likely typos.
generic:
selects: [] # only untagged (common) content
pages: 1| Definition | Behavior |
|---|---|
No selects | All content (no filter) |
selects: [frontend, backend] | Untagged + @frontend + @backend |
selects: [] | Untagged content only |
Variables
Tags filter content. Variables inject new per-application text into {{ }} placeholders:
# Jane Doe
jane@example.com | github.com/jane
{{ tagline }}# stripe-swe.view.yaml
stripe-swe:
selects: [backend]
vars:
tagline: 'Backend engineer specializing in stream processing'When defined, the value renders in place. When undefined, the placeholder produces nothing (no blank space). Variable values can contain markdown formatting (e.g. **bold**), which is rendered normally since substitution happens before markdown parsing.
Defining a variable with no matching {{ }} placeholder is an error.
Variables resolve in order: ephemeral view (CLI -v) > tag view or custom view vars > default view vars.
Sections
sections is a namespace for controlling which sections appear and how they're ordered. It contains two sub-fields:
hideremoves sections from the output. Everything not hidden renders in source order by default.pinmoves sections to the top of the document in the specified order. Non-pinned sections follow in their original source order.
Both fields accept data-section type values (e.g. work, skills, education). If you use a common synonym like experience, Resumx suggests the canonical name.
sections:
hide: [publications, volunteer]
pin: [skills, work]This hides publications and volunteer sections, pins skills first and work second, then everything else follows in source order. The header always renders regardless of configuration.
A section cannot appear in both hide and pin. If it does, Resumx raises an error.
On the CLI, use --hide and --pin flags (they map to sections.hide and sections.pin internally):
resumx resume.md --hide publications --pin skills,workBullet Order
bullet-order controls how bullets are arranged within each section.
| Value | Behavior |
|---|---|
none | Document order, as written in markdown. (default) |
tag | Tagged bullets promoted to top, sorted by selects declaration order within the group. |
Given:
- Led team of 5 engineers to deliver project 2 weeks early
- Designed event-driven microservices handling 2M events/day {.@distributed-systems}
- Built REST APIs with OpenAPI documentation {.@backend}With bullet-order: tag and selects: [backend, distributed-systems], tagged bullets float to the top, sorted by the selects declaration order (backend before distributed-systems):
- Built REST APIs with OpenAPI documentation {.@backend}
- Designed event-driven microservices handling 2M events/day {.@distributed-systems}
- Led team of 5 engineers to deliver project 2 weeks earlyThe recruiter's 7.4-second scan hits the most relevant content first, without rearranging anything in the source file.
Ephemeral Views
CLI flags like -v, --hide, --pin, --pages, --bullet-order, and -s create an ephemeral view inline without persisting it. Useful for quick iteration, scripting, and CI pipelines:
resumx resume.md --for backend -v tagline="Stream Processing, Go" --pin skills,work -o stripe.pdfThis is functionally a view with selects: [backend], vars.tagline, sections.pin, and output, it just doesn't live in a file.
Cascade Order
Views stack in layers, like the CSS cascade. Each layer can override specific fields from the layer below. Fields not set in a layer fall through to the next one down.
Built-in defaults
→ Default view (frontmatter render fields)
→ Tag view OR Custom view (whichever --for resolves)
→ Ephemeral view (CLI flags)Tag views and custom views are at the same level. --for resolves to exactly one of them, and that view overrides the default view. If the name matches both a tag and a custom view, Resumx raises an error. Ephemeral view (CLI flags) overrides everything.
How Fields Merge
Not all fields merge the same way. The rule depends on the field's shape:
| Field shape | Fields | Merge rule |
|---|---|---|
| Scalar | pages, bullet-order, format, output | Later layer replaces |
| Record | vars, style | Shallow merge (later keys override, earlier keys preserved) |
| Namespace | sections | Each sub-field (hide, pin) replaces independently |
| Array | selects, css | Later layer replaces (not concatenated) |
For example, if the default view sets vars: { tagline: "Full-stack engineer", keywords: "React" } and a custom view sets vars: { tagline: "Backend engineer" }, the resolved result is { tagline: "Backend engineer", keywords: "React" }. The custom view's tagline overrides, the default view's keywords is preserved.
For sections, each sub-field replaces independently. Setting sections: { pin: [skills] } in a child view replaces only pin, the parent's hide is preserved. Setting sections: { hide: [] } in a child view means "un-hide everything" without affecting pin.
Batch Rendering
Render all views at once, or use a glob for a subset:
resumx resume.md --for '*'
resumx resume.md --for 'stripe-*'To include the default view (no tag filtering) together with specific views, use --for default and list the named views. The name default is reserved; do not use it for a tag or custom view. Example: resumx resume.md --for default,frontend produces both resume.pdf (all content) and resume-frontend.pdf (frontend-filtered).
Glob patterns match against all named views (both tag views and custom views). The pattern must match at least one view, otherwise Resumx raises an error listing available names.