# Search Ad Preview

> A text ad as it appears in search results, on phone and desktop, with the length limits checked live.

Component · Admin · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/SearchAdPreview/)

A text ad as it appears in search results, on phone and desktop, with the length limits checked live.

Props: `name`, `domain`, `paths` (up to 2), `headlines` (up to 3 shown), `descriptions` (up to 2), `sitelinks`, `device` / `onDevice`, `sponsoredLabel`.

- Limits: headline 30, description 90, path 15 characters; anything longer is listed as a warning under the preview.
- The preview imitates the result layout in neutral colours, not the search engine's brand.

## Example

```tsx
import * as React from "react";
import { SearchAdPreview, TextField } from "@twentyfirstmedia/pro";

function App() {
  const hl = React.useState([
      "Real Instagram Followers",
      "Delivery in 1 Hour",
      "Trusted by 50,000 Creators",
    ]),
    ds = React.useState([
      "Grow your account with real followers. Secure payment, 30-day refill guarantee, German support.",
      "Start from €2.90. No password needed.",
    ]);
  return (
    <div
      className="tf-grid"
      style={
        {
          "--cols": "1fr 1fr",
          gap: 16,
          alignItems: "start",
        } as React.CSSProperties
      }
    >
      <div
        className="tf-panel"
        style={{
          gap: 12,
        }}
      >
        {hl[0].map((v, i) => (
          <TextField
            key={i}
            label={"Headline " + (i + 1)}
            value={v}
            onChange={(e) => {
              const n = hl[0].slice();
              n[i] = e.target.value;
              hl[1](n);
            }}
            hint={v.length + " / 30"}
          />
        ))}
        {ds[0].map((v, i) => (
          <TextField
            key={"d" + i}
            label={"Description " + (i + 1)}
            value={v}
            onChange={(e) => {
              const n = ds[0].slice();
              n[i] = e.target.value;
              ds[1](n);
            }}
            hint={v.length + " / 90"}
          />
        ))}
      </div>
      <div className="tf-panel">
        <SearchAdPreview
          name="Northwind"
          domain="northwind.de"
          paths={["instagram", "followers"]}
          headlines={hl[0]}
          descriptions={ds[0]}
          sitelinks={["Buy likes", "Pricing", "Refill guarantee", "Support"]}
        />
      </div>
    </div>
  );
}
export default App;
```

## Props

### SearchAdPreview

| Prop | Type | Notes |
| --- | --- | --- |
| `name?` | `string` |  |
| `domain` | `string` | Required |
| `paths?` | `string[]` |  |
| `headlines?` | `string[]` |  |
| `descriptions?` | `string[]` |  |
| `sitelinks?` | `string[]` |  |
| `device?` | `"mobile" \| "desktop"` |  |
| `onDevice?` | `(d: string) => void` |  |
| `sponsoredLabel?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### TextField

| Prop | Type | Notes |
| --- | --- | --- |
| `type?` | `string` | Input type (`"text"` by default): `"email"`, `"number"`, `"tel"`, `"url"`, `"password"` … |
| `icon?` | `IconName` |  |
| `suffix?` | `ReactNode` |  |
| `value?` | `string` |  |
| `defaultValue?` | `string` |  |
| `placeholder?` | `string` |  |
| `disabled?` | `boolean` |  |
| `readOnly?` | `boolean` |  |
| `autoComplete?` | `string` |  |
| `autoFocus?` | `boolean` |  |
| `min?` | `number \| string` | Smallest value for `type="number"`, `"date"` and the like (native `min`). |
| `max?` | `number \| string` | Largest value (native `max`). |
| `step?` | `number \| string` | Step between valid values (native `step`), e.g. `1` for whole numbers or `"any"`. |
| `minLength?` | `number` | Fewest characters (native `minlength`). |
| `maxLength?` | `number` | Most characters (native `maxlength`). |
| `pattern?` | `string` | Pattern the value must match (native `pattern`). |
| `inputMode?` | `"none" \| "text" \| "tel" \| "url" \| "email" \| "numeric" \| "decimal" \| "search"` | Virtual keyboard on touch devices (native `inputmode`), e.g. `"numeric"` or `"decimal"`. |
| `onChange?` | `(e: ChangeEvent<HTMLInputElement>) => void` |  |
| `onBlur?` | `(e: ChangeEvent<HTMLInputElement>) => void` |  |
| `onKeyDown?` | `(e: KeyboardEvent<HTMLInputElement>) => void` |  |
| `action?` | `ReactNode` | An interactive control at the end of the field (an `IconButton`: clear, copy, locate …). Unlike `suffix` it can be clicked, and it stays when the field shows an error or success mark. |
| `reveal?` | `boolean` | For `type="password"`: a button that shows and hides the password (default true). |
| `label?` | `string` |  |
| `hint?` | `string` |  |
| `error?` | `string` |  |
