# Linkified Text

Turns the live http and https URLs in a plain string into External Links and leaves defanged ones as text.

```tsx
import { LinkifiedText } from "@zenoxai/ui/patterns/linkified-text"

function LinkifiedTextDemo() {
  return (
    <p className="max-w-lg text-sm text-text-secondary">
      <LinkifiedText>
        {
          "Takedown requested through https://abuse.registrar.example/report on Oct 23. The kit itself is at hxxps://login-verify-acme[.]com/portal and stays text."
        }
      </LinkifiedText>
    </p>
  )
}
```

## Usage

```tsx
import { LinkifiedText } from "@zenoxai/ui/patterns/linkified-text"

<p className="text-sm text-text-secondary">
  <LinkifiedText>{comment.body}</LinkifiedText>
</p>
```

Use it on prose that may hold URLs: analyst comments, descriptions, report text. The text stays
exactly as written; each live http or https URL becomes an [External Link](/patterns/external-link)
with its disclosure.

- A defanged or partly defanged URL (`hxxps://evil[.]example`, `https://paypal.com[.]evil.example`)
  is never linked, and never trimmed into a link: in threat data it means "do not click".
- A host without a scheme (`www.acme.example`) stays text.
- Trailing punctuation and unbalanced brackets stay outside the link, as in GitHub's autolinks:
  "see https://acme.example/a)." links `https://acme.example/a`.

Detection is [`findIndicators`](/utilities/defang), so the two never disagree on what a URL is.
It takes a string only: Markdown and HTML need their own renderer, which can render External Link
for its links. Never put Linkified Text on an indicator column or field.

## Examples

### Keeping indicators out

Prose about an alert often repeats its indicators. Pass `isLinkable` with the record's own
indicators: those URLs render in `font-mono`, following `zx/inline-code`, and never as a link.

```tsx
import { LinkifiedText } from "@zenoxai/ui/patterns/linkified-text"

function LinkifiedTextIndicators() {
  const indicators = new Set(["https://login-verify-acme.com/portal"])

  return (
    <p className="max-w-lg text-sm text-text-secondary">
      <LinkifiedText isLinkable={(url) => !indicators.has(url.href)}>
        {
          "First seen at https://login-verify-acme.com/portal, reported to https://abuse.registrar.example/report."
        }
      </LinkifiedText>
    </p>
  )
}
```

## Accessibility

- Each link follows External Link's contract: a real link, a disclosure that takes focus, a new
  tab.
- The URL is shown as written, so the link's name is the URL the user reads.

## API reference

`import { ... } from "@zenoxai/ui/patterns/linkified-text"`

### LinkifiedText

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| isLinkable | `((url: URL) => boolean)` |  | Whether a URL may become a link. Return `false` for the indicators of the record on screen: they render as monospace text, never as a link. |
| labels | `Partial<ExternalLinkLabels>` |  | Forwarded to every link: the disclosure's translated texts. |
| underline | `"hover" \| "always"` |  | Forwarded to every link. |

