Link
import {Field} from 'alinea'
Field.link('Button link')
Field.link.multiple('Related links', {max: 5})Options
fields: extra fields stored on each link, such as a label or an "open in new tab" toggle. Pass an object of fields or a type.max(multiple only): the maximum number of links.allowDuplicates(multiple only): allow the same link more than once. DefaulttrueforField.link.multiple.conditionandlocation: limit which entries the page picker offers and where it opens, see the Entry field. They don't affect the file picker.The common options
help,width,inline,initialValue,required,validate,readOnly,hiddenandshared.
Allowing only some kinds of links
Field.link accepts all three kinds. To accept a single kind, use the field made for it:
Pages, external urls and files:
Field.linkOnly pages (internal links):
Field.entryOnly external urls:
Field.urlOnly files from the media library:
Field.fileOnly images:
Field.image
Each has the same .multiple variant, a narrower value type and a picker without the tabs for the other kinds.
Field.link has no option to hide one of its kinds. To accept two of them, such as a page or a url but no file, reject the third in validate. The stored link's _type is 'entry', 'url' or 'file':
import {Field} from 'alinea'
// A page or an external url, but no file
Field.link('Button link', {
validate(link) {
if (link?._type === 'file') return 'Link to a page or a url'
}
})To offer only some pages, pass a condition. It applies to the page picker, the url and file pickers are unaffected:
import {Field} from 'alinea'
Field.link('Button link', {
// Only offer pages of these types in the page picker
condition: {_type: {in: ['Page', 'BlogPost']}}
})Value
When you query a link field, every link is resolved to what you need to render it. Check _type to see which kind it is:
'entry': a page.hrefandurlhold its current url (so the link survives moves and renames), plustitle,path,entryIdandentryType.'url': an external link with theurlandhref(the same value),titleandtarget('_blank'or'_self') the editor entered.'file': an uploaded file with itsurlandhref(the same value),title,extensionandsizein bytes.
The values of the extra fields are on link.fields. A single link is null while it's empty, a multiple link field is an array.
import type {Link as LinkValue} from 'alinea'
import Link from 'next/link'
interface ButtonProps {
link: LinkValue<{label: string}> | null
}
export function Button({link}: ButtonProps) {
if (!link?.href) return null
const label = link.fields.label || link.title
if (link._type === 'url')
return (
<a href={link.href} target={link.target || undefined}>
{label}
</a>
)
return <Link href={link.href}>{label}</Link>
}Links to entries that were deleted, or that aren't published, are dropped from multiple link fields. A single link to such an entry keeps its stored reference but has no href, so always check href before rendering.
Extra fields
import {Field} from 'alinea'
Field.link('Call to action', {
fields: {
label: Field.text('Label', {width: 0.5}),
style: Field.select('Style', {
width: 0.5,
initialValue: 'primary',
options: {primary: 'Primary', secondary: 'Secondary'}
})
}
})Editors fill in these fields in the link's row after picking the target. LinkValue<{label: string}> in the example above types them; see Infer to derive the type from the fields instead.