> ## Documentation Index
> Fetch the complete documentation index at: https://docs.primo.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Working with Fields

> Add content fields to blocks and pages to make them editable.

Fields are what transform your components into editable blocks. You write the component code, then attach fields to make specific parts editable by content editors. This guide shows you how to add and configure fields effectively.

## Understanding Fields

Fields bridge the gap between code and content:

* **Component code** defines structure and styling
* **Fields** define what content editors can edit
* **Together** they create blocks that are both powerful and editable

<Info>
  Fields can be added to both blocks (for component-level editing) and page types (for page-level metadata). The process is the same for both.
</Info>

## Adding Fields to a Block

<Steps>
  <Step title="Create or open your component">
    In the Component Library, create a new block or open an existing one.
  </Step>

  <Step title="Write your component code">
    Build your component. Fields are globally available—no need to declare them:

    ```svelte theme={null}
    <section class="hero">
      <h1>{headline}</h1>
      <p>{description}</p>
      {#if image?.url}
        <img src={image.url} alt={image.alt} />
      {/if}
    </section>
    ```

    <Tip>
      Fields are automatically available in your component code. Just reference them directly like `{headline}` or `{image.url}`.
    </Tip>
  </Step>

  <Step title="Open the field editor">
    Click "Add Field" or the fields panel in the component editor.
  </Step>

  <Step title="Create your first field">
    Configure the field:

    * **Field name**: Must match your prop name (e.g., `headline`)
    * **Field type**: Choose the appropriate type (e.g., Text, Rich Text, Image)
    * **Label**: Display name for editors (e.g., "Headline")
    * **Default value**: Optional pre-filled value

    <Check>
      The field name must exactly match the prop name in your component code.
    </Check>
  </Step>

  <Step title="Add more fields">
    Repeat for each prop you want to make editable:

    * `description` → Rich Text field
    * `image` → Image field

    <Info>
      You don't need to create fields for every prop. Only create fields for content you want editors to control.
    </Info>
  </Step>

  <Step title="Save your block">
    Click "Save Block" to save your component with its fields.
  </Step>
</Steps>

## Field Naming

When you create a field, the field name determines how you reference it in your component:

```svelte theme={null}
<!-- If you create a field named "cta_text" -->
<button>{cta_text}</button>
```

**Field configuration:**

* **Field name**: `cta_text` (how you reference it in code)
* **Label**: "CTA Text" (what editors see in the UI)

<Warning>
  Field names are case-sensitive. `cta_text` and `ctaText` are different variables in your component.
</Warning>

## Common Field Patterns

### Hero Block

A typical hero block might use these fields in the template:

```svelte theme={null}
<section class="hero" style={background_image?.url ? `background-image: url(${background_image.url})` : ''}>
  <h1>{headline}</h1>
  <p>{subheadline}</p>
  {#if cta_link?.url}
    <a href={cta_link.url}>{cta_link.label}</a>
  {/if}
</section>
```

**Fields to create:**

* `headline` (Text) - Main headline
* `subheadline` (Text) - Supporting text
* `cta_link` (Link) - Button link with label
* `background_image` (Image, optional) - Background image

### Text Block

A simple text block:

```svelte theme={null}
<div class="text-{alignment}">
  {@html content}
</div>
```

**Fields to create:**

* `content` (Rich Text) - The main content
* `alignment` (Select) - Options: "left", "center", "right"

### Image Block

An image with caption:

```svelte theme={null}
<figure>
  {#if image?.url}
    <img src={image.url} alt={image.alt} />
  {/if}
  {#if caption}
    <figcaption>{caption}</figcaption>
  {/if}
</figure>
```

**Fields to create:**

* `image` (Image) - The image (includes URL and alt text)
* `caption` (Text, optional) - Image caption

### Card Grid Block

A grid of cards using a repeater:

```svelte theme={null}
<div class="grid cols-{columns}">
  {#each cards as card}
    <div class="card">
      {#if card.image?.url}
        <img src={card.image.url} alt={card.image.alt || card.title} />
      {/if}
      <h3>{card.title}</h3>
      <p>{card.description}</p>
      {#if card.link?.url}
        <a href={card.link.url}>{card.link.label || "Learn more"}</a>
      {/if}
    </div>
  {/each}
</div>
```

**Fields to create:**

* `cards` (Repeater) with nested fields:
  * `title` (Text)
  * `description` (Text)
  * `image` (Image, optional)
  * `link` (Link, optional)
* `columns` (Number) - Number of columns

## Template Syntax Guide

Primo blocks use Svelte 5 syntax. Here's the essential syntax you'll use most often when working with fields.

### Basic Interpolation

Display field values in your template:

```svelte theme={null}
<h1>{headline}</h1>
<p>{description}</p>

<!-- Image fields have .url and .alt properties -->
<img src={image.url} alt={image.alt} />
```

### Fallback Values

Provide default values when fields are empty:

```svelte theme={null}
<!-- Using || operator -->
<h1>{headline || "Welcome"}</h1>

<!-- Using ?? for null/undefined only -->
<p>{description ?? "No description available"}</p>
```

### Conditionals

Show or hide elements based on field values:

```svelte theme={null}
<!-- Simple if -->
{#if image?.url}
  <img src={image.url} alt={image.alt} />
{/if}

<!-- If/else -->
{#if featured}
  <span class="badge">Featured</span>
{:else}
  <span class="badge">Standard</span>
{/if}

<!-- Multiple conditions -->
{#if variant === "primary"}
  <button class="btn-primary">{cta_text}</button>
{:else if variant === "secondary"}
  <button class="btn-secondary">{cta_text}</button>
{:else}
  <button class="btn-default">{cta_text}</button>
{/if}
```

### Loops (Repeater Fields)

Iterate over repeater field arrays:

```svelte theme={null}
<!-- Basic loop -->
{#each items as item}
  <div class="item">
    <h3>{item.title}</h3>
    <p>{item.description}</p>
  </div>
{/each}

<!-- With index -->
{#each features as feature, index}
  <div class="feature">
    <span class="number">{index + 1}</span>
    <h3>{feature.title}</h3>
  </div>
{/each}

<!-- With else (when array is empty) -->
{#each items as item}
  <div>{item.name}</div>
{:else}
  <p>No items to display</p>
{/each}
```

### HTML Rendering

Render HTML from rich text or markdown fields:

```svelte theme={null}
<!-- Rich text content -->
<div class="content">
  {@html content}
</div>

<!-- Markdown content (processed automatically) -->
<article class="markdown">
  {@html markdown_content}
</article>
```

<Info>
  Rich text and markdown fields render HTML using `{@html}`. Since content comes from trusted editors (not site visitors), this is safe to use.
</Info>

### Reactive Statements

Compute derived values from fields:

```svelte theme={null}
<script>
  // Computed value based on fields
  $: fullName = `${firstName} ${lastName}`;

  // Computed array from repeater
  $: featuredItems = items.filter(item => item.featured);

  // Multiple statements
  $: {
    const total = items.reduce((sum, item) => sum + item.price, 0);
    console.log('Total:', total);
  }
</script>

<h1>Welcome, {fullName}</h1>

{#each featuredItems as item}
  <div>{item.title}</div>
{/each}
```

### Event Handlers

Handle user interactions:

```svelte theme={null}
<script>
  function handleClick() {
    console.log('Button clicked');
    // Analytics, tracking, etc.
  }

  function handleSubmit(event) {
    event.preventDefault();
    // Handle form submission
  }
</script>

<button on:click={handleClick}>{cta_text}</button>

<form on:submit={handleSubmit}>
  <!-- form fields -->
</form>
```

### Class Directives

Apply classes conditionally:

```svelte theme={null}
<!-- Single class -->
<div class:active={isActive}>Content</div>

<!-- Multiple classes -->
<div
  class:primary={variant === "primary"}
  class:large={size === "large"}
  class:featured={featured}
>
  Content
</div>

<!-- Shorthand (when variable matches class name) -->
<div class:featured>Content</div>
```

### Style Directives

Apply inline styles dynamically:

```svelte theme={null}
<!-- Single style -->
<div style:color={textColor}>Colored text</div>

<!-- Multiple styles -->
<div
  style:background-color={backgroundColor}
  style:color={textColor}
  style:padding="{spacing}rem"
>
  Styled content
</div>

<!-- CSS custom properties -->
<div style:--theme-color={themeColor}>
  Content with CSS variable
</div>
```

### Snippets (Reusable Templates)

Create reusable template chunks within a block:

```svelte theme={null}
{#snippet card(title, description)}
  <div class="card">
    <h3>{title}</h3>
    <p>{description}</p>
  </div>
{/snippet}

<!-- Use the snippet -->
{@render card("First Card", "Description here")}
{@render card("Second Card", "Another description")}

<!-- Use in loops -->
{#each items as item}
  {@render card(item.title, item.description)}
{/each}
```

### What You Won't Need

These Svelte features are **not relevant** for Primo blocks:

* **Stores** - No need for cross-component state; fields are globally available within each block
* **Context API** - Use relational fields instead (Page Field, Site Field, Page, Page List) to reference data from elsewhere
* **\$bindable** - Only for component authoring, not block templates
* **Custom elements** - Blocks compile to standard components
* **SvelteKit features** - Routing, load functions, etc. (Primo handles this)
* **Component slots** - Blocks don't accept slotted content

### Learn More

For advanced Svelte features and patterns, see the [Svelte 5 documentation](https://svelte.dev/docs/svelte/overview).

## Field Configuration

### Required Fields

Mark fields as required when they're essential:

<Example>
  A Hero block's `headline` field should be required—a hero without a headline doesn't make sense.
</Example>

**When to require:**

* Essential content (headlines, titles)
* Fields that affect functionality
* Fields needed for proper display

**When not to require:**

* Optional enhancements (images, captions)
* Fields with sensible defaults
* Decorative elements

### Default Values

You can set default values in the field configuration, or handle empty values in your template:

```svelte theme={null}
<!-- Option 1: Use field defaults (set in field config) -->
<h1>{headline}</h1>

<!-- Option 2: Provide fallback values in template -->
<h1>{headline || "Welcome"}</h1>

<!-- Option 3: Conditionally render -->
{#if headline}
  <h1>{headline}</h1>
{/if}
```

**Best practices:**

* Set sensible defaults in field configuration
* Handle empty/missing values gracefully
* Make components functional even with minimal content

### Help Text

Add help text to guide editors:

<Example>
  **Field**: `background_image`
  **Help text**: "Optional background image. Recommended size: 1920x1080px. Will be used as a fallback if no image is provided."
</Example>

**Good help text:**

* Explains what the field is for
* Provides guidance on content
* Mentions any requirements (size, format, etc.)

### Placeholders

Use placeholders to show example content:

* **Headline field**: "Enter your headline here"
* **Description field**: "Describe your product or service"
* **Link field**: "/about" or "[https://example.com](https://example.com)"

<Tip>
  Placeholders help editors understand what to enter, especially for fields like URLs or slugs.
</Tip>

## Advanced Field Patterns

<AccordionGroup>
  <Accordion title="Conditional Fields">
    ### Conditional Fields

    Some fields should only appear when others have values:

    <Example>
      A "Video" block might have:

      * `video_type` (Select): "Upload" or "Embed"
      * `video_file` (Video): Show only when `video_type` is "Upload"
      * `video_url` (Text): Show only when `video_type` is "Embed"
    </Example>

    **Setting up conditionals:**

    1. Create the control field (e.g., `video_type`)
    2. Configure dependent fields to show/hide based on the control field
    3. Test with different values
  </Accordion>

  <Accordion title="Repeater Fields">
    ### Repeater Fields

    Repeaters let editors add multiple items:

    <Steps>
      <Step title="Create the repeater field">
        Add a Repeater field (e.g., `features`).
      </Step>

      <Step title="Add nested fields">
        Inside the repeater, add fields for each item:

        * `title` (Text)
        * `description` (Text)
        * `icon` (Image, optional)
      </Step>

      <Step title="Use in component">
        Reference the repeater field as an array:

        ```svelte theme={null}
        {#each features as feature}
          <div class="feature">
            <h3>{feature.title}</h3>
            <p>{feature.description}</p>
          </div>
        {/each}
        ```
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Group Fields">
    ### Group Fields

    Groups organize related fields:

    <Steps>
      <Step title="Create a group field">
        Add a Group field (e.g., `author`).
      </Step>

      <Step title="Add fields to the group">
        Inside the group, add related fields:

        * `name` (Text)
        * `email` (Email)
        * `bio` (Textarea)
      </Step>

      <Step title="Use in component">
        Reference the group field as an object:

        ```svelte theme={null}
        <div class="author">
          <h3>{author.name}</h3>
          <p>{author.bio}</p>
          <a href="mailto:{author.email}">Contact</a>
        </div>
        ```
      </Step>
    </Steps>
  </Accordion>
</AccordionGroup>

## Field Validation

### Text Validation

Add validation rules to text fields:

* **Min length**: Minimum character count
* **Max length**: Maximum character count
* **Pattern**: Regex pattern for format validation

<Example>
  An email field might use pattern: `^[^\s@]+@[^\s@]+\.[^\s@]+$`
</Example>

### Number Validation

For number fields:

* **Min value**: Minimum allowed number
* **Max value**: Maximum allowed number
* **Step**: Increment/decrement step

<Example>
  A "Price" field might have:

  * Min: 0
  * Max: 10000
  * Step: 0.01 (for cents)
</Example>

### File Validation

For image and file fields:

* **Allowed formats**: File extensions (jpg, png, pdf, etc.)
* **Max file size**: Maximum upload size in MB
* **Dimensions**: Min/max width and height (for images)

## Testing Fields

### Test with Empty Values

Always test your components with empty fields:

```svelte theme={null}
{#if headline}
  <h1>{headline}</h1>
{/if}

{#if image?.url}
  <img src={image.url} alt={image.alt} />
{/if}
```

### Test with Default Values

Verify defaults work (if you set them in field configuration):

1. Create a page with your block
2. Don't fill in any fields
3. Check that the component displays correctly with default values

### Test Edge Cases

Try:

* Very long text
* Special characters
* Empty arrays (for repeaters)
* Missing nested fields (for groups)

## Best Practices

### 1. Start Simple

Begin with essential fields, add more as needed:

```yaml theme={null}
Simple Hero:
  Fields: headline, subheadline, cta_text

Enhanced Hero:
  Fields: headline, subheadline, cta_text, background_image, overlay_color
```

### 2. Use Descriptive Labels

Labels are what editors see:

* ✅ "Featured Image"
* ✅ "Publication Date"
* ✅ "Author Name"
* ❌ "Field 1"
* ❌ "Data"

### 3. Provide Helpful Defaults

Set defaults in field configuration to make components work immediately:

* **Number fields**: Set sensible defaults (e.g., `columns` defaults to 3)
* **Boolean fields**: Set appropriate defaults (e.g., `show_title` defaults to true)
* **Select fields**: Pre-select the most common option (e.g., `alignment` defaults to "left")

### 4. Group Related Fields

Use Groups or organize fields logically:

* **Content fields**: headline, description, content
* **Media fields**: image, video, background
* **Settings fields**: alignment, columns, variant

### 5. Validate Input

Add validation to prevent errors:

* Required fields for essential content
* Format validation for emails, URLs
* Size limits for files and text

## Common Mistakes

### Mismatched Names

Field names must match how you reference them in code:

```svelte theme={null}
<!-- If field is named "ctaText" in field config -->
<button>{ctaText}</button>

<!-- Not "cta_text" - field names are case-sensitive -->
```

### Not Handling Empty Values

Always handle missing or empty field values:

```svelte theme={null}
<!-- Bad: Breaks if headline is empty -->
<h1>{headline}</h1>

<!-- Good: Handles empty values -->
{#if headline}
  <h1>{headline}</h1>
{/if}

<!-- Also good: Provides fallback -->
<h1>{headline || "Welcome"}</h1>
```

### Over-Complicating

Start simple, add complexity later:

```yaml theme={null}
# Start here
Fields: title, description

# Add later if needed
Fields: title, description, image, link, category, tags, featured
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Field Types Reference" icon="book" href="/reference/field-types">
    Complete reference of all available field types.
  </Card>

  <Card title="Writing Components" icon="code" href="/building-sites/writing-components">
    Learn how to write components that work well with fields.
  </Card>

  <Card title="Building from Scratch" icon="rocket" href="/building-sites/your-first-site">
    See fields in action with a complete site example.
  </Card>

  <Card title="Defining Page Types" icon="layer-group" href="/building-sites/defining-page-types">
    Add page-level fields to page types.
  </Card>
</CardGroup>

<Tip>
  The best fields are the ones editors actually use. Start with essential fields, then add more based on feedback from content editors.
</Tip>
