# Variables

> Turn a component's text, image, link or style into a prop from the canvas, so each use of the component sets its own. The code before and after.

- Page: https://midcode.app/docs/editor/variables
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt

A variable is a prop of a component that you make from the canvas instead of typing it. A card that always says "Oak House" becomes a card whose title each page sets: you press a plus beside the text, and midcode declares the prop, gives it the text as its default, and makes the markup read it.

Nothing changes on screen when you make one. The value the element had is the prop's default, so every existing use of the component looks the same until one of them sets its own.

Variables are made in [component mode](https://midcode.app/docs/editor/components.md), in React components. From the next release, in [Svelte, Vue and Astro components](#svelte-vue-and-astro) too.

## Make a variable

1. Open the component (double-click an instance, or click it in Assets).
2. Select an element inside it.
3. In the right panel, find the property and press its plus, "Make it a variable". Beside Text, Link, Image and Video the plus is in the section's header. Beside a style it shows to the left of the row when you point at it.

These are the properties that have one:

| Property | Becomes | Name it starts with |
| --- | --- | --- |
| Text | The element's text | `title` on a heading, `label` on a button or a link, `text` elsewhere |
| Link | Its `href` | `link` |
| Image, Video | Its `src` | `image`, `video` |
| Color | An inline `color` | `color` |
| Background | An inline `backgroundColor` | `background` |
| Padding | An inline `padding` | `padding` |
| Gap | An inline `gap` | `gap` |
| Opacity | An inline `opacity` | `opacity` |
| Radius | An inline `borderRadius` | `radius` |

You aren't asked for a name. If the component already has a prop called `title`, the new one is `title2`; rename it afterwards in the Variables window.

Once a property is a variable, its control is replaced by a violet chip with the variable's name: the value is no longer the component's to set. Click the chip to edit the variable, or right-click it → "Remove variable".

## Set it where the component is used

Go back to the page (`Esc`), select an instance of the component, and look under "Props" in the right panel. Each variable is a control of its kind: a text field, an image picker, a color, a number. What you set there is written on that instance:

```diff title="app/page.tsx"
-<Card href="/work/pine" />
+<Card href="/work/pine" title="Pine Cabin" image="/images/pine.jpg" radius={24} />
```

How those controls are chosen is on [Components](https://midcode.app/docs/editor/components.md).

## The Variables window

The "Variables" button in the bar over the component canvas opens a window with every variable of the component (it shows how many there are). Pick one on the left to:

- change its **Name**. The rename follows the prop everywhere: its type, where the component reads it, and every instance that sets it.
- see its **Kind**: Text, Number, Color, Image or Link (a prop you wrote by hand can also be Yes / No, Choice or Code).
- change its **Default**, the value the component shows until a use sets it.
- see what it's **Used by**: a text, an attribute, a style.
- **Remove variable**.

The list is the component's props, so props you wrote by hand are there too. Props the component only passes on (`children`, `className`, `style`), event handlers and `variant` are left out.

## What midcode writes

Each of these is one edit, and one `⌘Z`. Start from this component:

```tsx title="components/Card.tsx"
interface CardProps {
  href: string
}

export function Card({ href }: CardProps) {
  return (
    <a href={href} className="block rounded-xl p-6">
      <img src="/images/oak.jpg" alt="" className="w-full" />
      <h3 className="text-xl font-semibold">Oak House</h3>
    </a>
  )
}
```

### A text

Select the heading and press the plus beside Text.

```diff title="components/Card.tsx"
 interface CardProps {
   href: string
+  title?: string
 }
 
-export function Card({ href }: CardProps) {
+export function Card({ href, title = 'Oak House' }: CardProps) {
   return (
     <a href={href} className="block rounded-xl p-6">
       <img src="/images/oak.jpg" alt="" className="w-full" />
-      <h3 className="text-xl font-semibold">Oak House</h3>
+      <h3 className="text-xl font-semibold">{title}</h3>
     </a>
   )
 }
```

### An attribute

Select the image and press the plus beside Image. The JSDoc line tells the Props section this string is an image, so instances get a file picker. A color gets `@control color`, a link `@control link`.

```diff title="components/Card.tsx"
 interface CardProps {
   href: string
   title?: string
+  /** @control image */
+  image?: string
 }
 
-export function Card({ href, title = 'Oak House' }: CardProps) {
+export function Card({ href, title = 'Oak House', image = '/images/oak.jpg' }: CardProps) {
   return (
     <a href={href} className="block rounded-xl p-6">
-      <img src="/images/oak.jpg" alt="" className="w-full" />
+      <img src={image} alt="" className="w-full" />
       <h3 className="text-xl font-semibold">{title}</h3>
     </a>
   )
 }
```

### A style

Select the card and press the plus beside Radius. A class can't take a value that's only known when the component renders, so a style variable is written inline, and the classes that used to set it are removed.

```diff title="components/Card.tsx"
   title?: string
   /** @control image */
   image?: string
+  radius?: number
 }
 
-export function Card({ href, title = 'Oak House', image = '/images/oak.jpg' }: CardProps) {
+export function Card({ href, title = 'Oak House', image = '/images/oak.jpg', radius = 12 }: CardProps) {
   return (
-    <a href={href} className="block rounded-xl p-6">
+    <a href={href} className="block p-6" style={{ borderRadius: radius }}>
```

The default is the radius the element had, in px. If the element already has a `style={{ … }}` object, the new key joins it.

### Other ways a component takes its props

midcode declares the prop the way the component already works:

- **No TypeScript**: there's no type to add to. The kind goes on the destructured name: `{ /** @control image */ image = '/images/oak.jpg' }`.
- **Props not destructured** (`function Card(props: CardProps)`): the markup reads `{props.title ?? 'Oak House'}`, and the type's member carries the default as `/** @default 'Oak House' */` so the Props section knows it.
- **No parameter yet** (`function Card()`): midcode adds one, `{ title = 'Oak House' }: { title?: string }`.

### Renaming

Renaming `title` to `heading` changes the type's member, the destructured name, every place the component reads it, and `title="…"` on every instance in the project.

### Removing

Removing a variable takes the prop out of the type and the destructuring, takes the attribute off every instance, and writes the default back where the prop was read: `{title}` is "Oak House" again, and `src={image}` is `src="/images/oak.jpg"` again.

A style variable is the exception. The inline style stays, with the default in it (`style={{ borderRadius: 12 }}`), and the class that was removed is not put back.

## Svelte, Vue and Astro (next release)

In the next release the plus is there in a `.svelte`, `.vue` or `.astro` component too, and the Variables window works the same. What changes is how each of them writes a prop being read:

| | Svelte | Vue | Astro |
| --- | --- | --- | --- |
| Text | `<h3>{title}</h3>` | `<h3>{{ title }}</h3>` | `<h3>{title}</h3>` |
| Attribute | `src={image}` | `:src="image"` | `src={image}` |
| Style | `style:border-radius="{radius}px"` | `:style="{ borderRadius: radius + 'px' }"` | `` style={{ borderRadius: `${radius}px` }} `` |

None of the three adds `px` to a number the way React does, so a length is written with its unit. Opacity stays a plain number.

The prop is declared where the component already declares its props:

- **Svelte**: in `$props()` with its type, as an `export let` in a component written that way, or in a new `<script>` when there's none.
- **Vue**: a member in the type of `defineProps<…>()` with its default in `withDefaults(…)` (put around `defineProps` when it isn't there) or in the destructuring; a property in `defineProps({ … })`; or the whole line in a component with no props yet.
- **Astro**: a member in `interface Props` and a default in `const { … } = Astro.props`. Both are added when the component has neither.

Renaming and removing follow every use of the component, as in React (`title="…"`, `:title="…"`, and `image-url` for `imageUrl` in a Vue template). They're refused when the component's own code also reads the prop, in a condition or a computed value for instance: midcode says "is also read by the component's own code", and the change is made there.

Not written: a Vue component on the Options API or one that lists its props as `defineProps([...])`.

## Limits

- In the released version variables are for React function components written in JSX, in a file midcode marks (`.tsx` and `.jsx` in Next.js and Vite projects, not a plain `.js` file). Svelte, Vue and Astro components come with the next release. Not class components.
- The element has to be written in the component's own file. An element that comes from another component inside it has no plus.
- Only plain text can become a variable. A text with code or other elements in it is refused ("Only a plain text can become a variable: this one has code or other elements in it").
- An attribute or a style that's already set from code can't be bound from the canvas: midcode says so and leaves it.
- A style variable replaces every class that set that property on the element, including its breakpoint, variant and state classes. One value from the instance wins everywhere.
- A padding variable is one number for all four sides. It starts from the element's top padding.
- A background image has no plus.
- A variable's name starts with a lowercase letter and has only letters and numbers (`title`, `imageUrl`). Names like `children`, `className`, `style` and `variant` are taken.
- Changing a default from the Variables window needs the prop to be destructured with its default. Otherwise midcode asks you to change it in the code.
