Skip to main content

Overview

Advanced elements provide complex functionality like dynamic lists, third-party integrations, and conditional forms. These elements unlock powerful features for sophisticated LinkApps.
ElementUse Case
arrayDynamic lists of structured items (FAQs, links, products)
integrationConnect third-party services (email marketing, etc.)
conditionalDisplayShow/hide elements based on other values
fileUpload images or documents
embedOptionConfigure embedded content display
buttonTrigger actions or workflows

Array

Dynamic array of structured inputs for collecting lists of items. Perfect for FAQs, social links, product lists, or any repeating data.

Properties

PropertyTypeDescription
idstringUnique identifier (becomes prop name)
inputType'array'Must be 'array'
titlestringField title
descriptionstringHelp text
labelstringLabel for the array
defaultValuearrayInitial array values
array_optionsobjectArray UI configuration
array_elementsarrayElement definitions for each item
validationobjectValidation rules

Array Options

Configure the array UI in the admin:
PropertyTypeDescription
minnumberMinimum items required
maxnumberMaximum items allowed
add_item_button_textstring”Add” button text (empty array)
add_second_item_button_textstring”Add” button text (after first item)
add_item_titlestringAdd dialog title
edit_item_titlestringEdit dialog title
item_formatstringDisplay template (e.g., {{question}})

Validation

RuleTypeDescription
requiredbooleanArray must have at least one item
maxSizenumberMaximum items (alternative to array_options.max)
linkapp.config.ts
{
  id: "socialLinks",
  inputType: "array",
  title: "Social Links",
  description: "Add your social media profiles",
  label: "Social profiles",
  array_options: {
    min: 1,
    max: 5,
    add_item_button_text: "Add social link",
    add_second_item_button_text: "Add another link",
    item_format: "{{platform}}: {{url}}"
  },
  array_elements: [
    {
      id: "platform",
      inputType: "select",
      title: "Platform",
      label: "Social platform",
      options: [
        { label: "Twitter", value: "twitter" },
        { label: "Instagram", value: "instagram" },
        { label: "TikTok", value: "tiktok" },
        { label: "LinkedIn", value: "linkedin" }
      ],
      validation: { required: true }
    },
    {
      id: "url",
      inputType: "url",
      title: "Profile URL",
      label: "URL",
      placeholder: "https://twitter.com/username",
      validation: { required: true }
    }
  ],
  validation: {
    required: true
  }
}
Usage in layout:
app/expanded.tsx
type SocialLink = {
  platform: 'twitter' | 'instagram' | 'tiktok' | 'linkedin'
  url: string
}

type Settings = {
  socialLinks: SocialLink[]
}

export default function ClassicLayout({ socialLinks }: AppProps<Settings>) {
  return (
    <div className="social-links">
      <h2>Follow me:</h2>
      {socialLinks.map((link, index) => (
        <a key={index} href={link.url} className={`social-${link.platform}`}>
          {link.platform}
        </a>
      ))}
    </div>
  )
}

Example: FAQ List

linkapp.config.ts
{
  id: "questions_list",
  inputType: "array",
  title: "Questions",
  validation: {
    required: true,
    maxSize: 20
  },
  array_options: {
    add_item_button_text: "Add a question",
    add_item_title: "Add question",
    add_second_item_button_text: "Add another question",
    edit_item_title: "Edit question",
    item_format: "{{question}}"  // Shows question text in list
  },
  array_elements: [
    {
      id: "question",
      title: "Question",
      inputType: "text",
      validation: {
        required: true,
        minLength: 1,
        maxLength: 200
      }
    },
    {
      id: "answer",
      title: "Answer",
      inputType: "textarea",
      validation: {
        required: true,
        minLength: 1,
        maxLength: 800
      }
    }
  ]
}
Usage in layout:
app/expanded.tsx
type Question = {
  question: string
  answer: string
}

type FAQSettings = {
  questions_list: Question[]
}

export default function ClassicLayout({ questions_list }: AppProps<FAQSettings>) {
  return (
    <div className="faq">
      <h1>Frequently Asked Questions</h1>
      {questions_list.map((item, index) => (
        <details key={index}>
          <summary>{item.question}</summary>
          <p>{item.answer}</p>
        </details>
      ))}
    </div>
  )
}
Use item_format with {{fieldId}} syntax to show a preview of each item in the admin list view.

Conditional Display

Show or hide elements based on the value of other elements. Creates dynamic forms that adapt to user choices.

Properties

PropertyTypeDescription
dependsOnstringID of the element this depends on
valuestring | booleanValue that triggers display
operator'equals' | 'notEquals' | 'contains'Comparison operator (default: 'equals')
Add conditionalDisplay to any element to control its visibility.

Example: Conditional Email Input

linkapp.config.ts
elements: [
  {
    id: "enableNotifications",
    inputType: "switch",
    title: "Notifications",
    label: "Enable email notifications",
    defaultValue: false
  },
  {
    id: "notificationEmail",
    inputType: "text",
    title: "Email Address",
    label: "Email",
    placeholder: "you@example.com",
    validation: {
      required: true
    },
    // Only show if enableNotifications is true
    conditionalDisplay: {
      dependsOn: "enableNotifications",
      value: true,
      operator: "equals"
    }
  }
]

Example: Conditional Based on Select

linkapp.config.ts
elements: [
  {
    id: "integrationProvider",
    inputType: "select",
    title: "Integration Provider",
    options: [
      { label: "Mailchimp", value: "mailchimp" },
      { label: "SendGrid", value: "sendgrid" },
      { label: "Custom API", value: "custom" }
    ]
  },
  {
    id: "customApiEndpoint",
    inputType: "url",
    title: "Custom API Endpoint",
    placeholder: "https://api.example.com/notify",
    validation: { required: true },
    // Only show if integrationProvider is 'custom'
    conditionalDisplay: {
      dependsOn: "integrationProvider",
      value: "custom"
    }
  },
  {
    id: "customApiKey",
    inputType: "password",
    title: "API Key",
    // Also only show for custom
    conditionalDisplay: {
      dependsOn: "integrationProvider",
      value: "custom"
    }
  }
]

Operators

OperatorDescriptionExample
equalsExact match (default)value: true shows when field is true
notEqualsDoes not matchvalue: "none" shows when field is not “none”
containsArray contains valuevalue: "advanced" shows if array includes “advanced”
Use conditional display to reduce form complexity and show users only relevant fields.

Integration

Connect to third-party services like email marketing platforms. Requires users to have the integration already set up in their Linktree account.

Properties

PropertyTypeDescription
idstringUnique identifier
inputType'integration'Must be 'integration'
capabilitystringRequired integration capability
vendorstringSpecific vendor identifier (optional)
titlestringField title
descriptionstringHelp text

Example

linkapp.config.ts
{
  id: "emailIntegration",
  inputType: "integration",
  title: "Email Marketing",
  description: "Connect your email marketing service to collect subscribers",
  capability: "MANAGE_EMAIL_SUBSCRIBERS",
  vendor: "mailchimp"  // Optional: specific vendor
}
Users must have the integration already connected in their Linktree account. This element lets them select which integration to use with your LinkApp.

File Upload

Upload images, documents, or other file types.

Properties

PropertyTypeDescription
idstringUnique identifier
inputType'file'Must be 'file'
titlestringField title
descriptionstringHelp text
labelstringInput label
acceptstring[]Accepted file types
multiplebooleanAllow multiple files
validationobjectValidation rules

Example

linkapp.config.ts
{
  id: "bannerImage",
  inputType: "file",
  title: "Banner Image",
  description: "Upload a banner image for your LinkApp",
  label: "Choose image",
  accept: ["image/png", "image/jpeg", "image/webp"],
  multiple: false,
  validation: {
    required: true
  }
}

Embed Option

Configure how embedded content should display. Specialized for LinkApps that embed third-party content.

Properties

PropertyTypeDescription
idstringUnique identifier
inputType'embedOption'Must be 'embedOption'
titlestringField title
descriptionstringHelp text
defaultValuestringDefault embed option

Example

linkapp.config.ts
{
  id: "embedDisplay",
  inputType: "embedOption",
  title: "Embed Display",
  description: "Choose how to display embedded content",
  defaultValue: "full"
}

Button

Interactive button element that triggers actions.

Properties

PropertyTypeDescription
idstringUnique identifier
inputType'button'Must be 'button'
labelstringButton text
actionobjectAction configuration

Action Properties

PropertyTypeDescription
on'click'Interaction event
type'update-link'Action type
data.link_typestringLink type to update to

Example

linkapp.config.ts
{
  id: "upgradeButton",
  inputType: "button",
  label: "Upgrade to Pro Version",
  action: {
    on: "click",
    type: "update-link",
    data: {
      link_type: "pro-version-app"
    }
  }
}

Complete Example: Advanced FAQ App

Here’s a complete example using arrays and conditional display:
linkapp.config.ts
export default {
  settings: {
    title: "FAQ Manager",
    icon: "question",
    uses_url: false,
    elements: [
      // Array of FAQ items
      {
        id: "questions_list",
        inputType: "array",
        validation: {
          required: true,
          maxSize: 20
        },
        array_options: {
          add_item_button_text: "Add a question",
          add_item_title: "Add question",
          item_format: "{{question}}"
        },
        array_elements: [
          {
            id: "question",
            title: "Question",
            inputType: "text",
            validation: { required: true, maxLength: 200 }
          },
          {
            id: "answer",
            title: "Answer",
            inputType: "textarea",
            validation: { required: true, maxLength: 800 }
          }
        ]
      },

      // Enable search toggle
      {
        id: "enableSearch",
        inputType: "switch",
        title: "Search Options",
        label: "Enable question search",
        defaultValue: false
      },

      // Search placeholder (conditional)
      {
        id: "searchPlaceholder",
        inputType: "text",
        title: "Search Placeholder",
        label: "Placeholder text",
        defaultValue: "Search questions...",
        conditionalDisplay: {
          dependsOn: "enableSearch",
          value: true
        }
      },

      // Enable categories toggle
      {
        id: "enableCategories",
        inputType: "switch",
        title: "Organization",
        label: "Group questions by category",
        defaultValue: false
      },

      // Category list (conditional)
      {
        id: "categories",
        inputType: "array",
        title: "Categories",
        description: "Define question categories",
        conditionalDisplay: {
          dependsOn: "enableCategories",
          value: true
        },
        array_options: {
          add_item_button_text: "Add category",
          item_format: "{{name}}"
        },
        array_elements: [
          {
            id: "name",
            inputType: "text",
            title: "Category Name",
            validation: { required: true, maxLength: 50 }
          }
        ]
      }
    ]
  }
}

Validation Strategies

Required Arrays

{
  id: "items",
  inputType: "array",
  validation: {
    required: true  // At least one item required
  }
}

Array Size Limits

{
  id: "items",
  inputType: "array",
  array_options: {
    min: 1,   // Minimum items
    max: 10   // Maximum items
  }
}
Or use validation.maxSize:
{
  id: "items",
  inputType: "array",
  validation: {
    maxSize: 10
  }
}

Nested Validation

Array elements can have their own validation:
array_elements: [
  {
    id: "email",
    inputType: "text",
    validation: {
      required: true,
      pattern: "^[^@]+@[^@]+\\.[^@]+$"  // Email format
    }
  }
]

Best Practices

Array Design

// ✅ Good - clear item format
array_options: {
  item_format: "{{question}}"  // Shows question text
}

// ✅ Good - descriptive button text
array_options: {
  add_item_button_text: "Add a social link",
  add_second_item_button_text: "Add another social link"
}

Conditional Display

// ✅ Good - progressive disclosure
{
  id: "advancedMode",
  inputType: "switch",
  label: "Show advanced options"
},
{
  id: "advancedSettings",
  inputType: "text",
  conditionalDisplay: {
    dependsOn: "advancedMode",
    value: true
  }
}

Array Element Names

// ✅ Good - descriptive IDs
array_elements: [
  { id: "question", inputType: "text" },
  { id: "answer", inputType: "textarea" }
]

// ❌ Avoid - unclear
array_elements: [
  { id: "q", inputType: "text" },
  { id: "a", inputType: "textarea" }
]

Type Safety

Define array item types:
type Question = {
  question: string
  answer: string
}

type SocialLink = {
  platform: string
  url: string
}

type Settings = {
  questions_list: Question[]
  socialLinks: SocialLink[]
  enableSearch: boolean
  searchPlaceholder: string  // Only shown if enableSearch is true
}

export default function ClassicLayout(props: AppProps<Settings>) {
  const { questions_list, socialLinks, enableSearch, searchPlaceholder } = props

  return <div>...</div>
}

Next Steps