@isomorph-agency/strapi-plugin-link
v0.1.1
Published
Strapi v5 Custom Field — Universal link picker (internal page, external URL, tel, mailto, anchor)
Maintainers
Readme
@isomorph-agency/strapi-plugin-link
Strapi v5 Custom Field — Universal link picker. One field, all link kinds : internal Content Type references, system pages, external URLs, phone numbers, emails, anchors.
Status: v0.1.0, published on npm (plugin + resolver). Tested on Strapi v5. Full presentation: isomorph.fr/plugins/link.
Why
Strapi v5 has no built-in "link" field. Every project ends up rolling its own — usually a custom component with an enum + 5 optional relations + a long string field. Painful to author, painful to query, painful to migrate.
This plugin gives you one link Custom Field per content. Authors pick a kind, fill one input, done. The shape stored in DB is a stable JSON, and the front gets a tiny resolveLink() helper that returns { href, label, target, rel }.
Features
- 6 kinds : Internal (any CT), System page, External URL, Phone, Email, Anchor
- Unified picker — one field, tabbed UI, one summary
- Slug-safe — internal references survive renaming the target's slug (route resolved by lookup, not stored URL)
- System pages — declared in plugin config, mapped to fixed routes (Home, Contact, etc. — perfect for Single Types in Strapi)
- Framework-agnostic front lib —
@isomorph-agency/link-resolverworks with Next.js, Nuxt, Astro, anything - Zod-validated server-side before persistence
- i18n-ready (admin translations in
fr,en) - TypeScript strict
Install
npm install @isomorph-agency/strapi-plugin-linkEnable in strapi/config/plugins.ts :
export default ({ env }) => ({
"@isomorph-agency/strapi-plugin-link": {
enabled: true,
config: {
// Routes URL by Content Type
contentTypes: {
"api::page.page": { route: "/:slug" },
"api::service.service": { route: "/services/:slug" },
"api::sous-service.sous-service": {
route: "/services/:service.slug/:slug",
populateForRoute: ["service"],
},
"api::realisation.realisation": {
route: "/realisations/:categorie.slug/:slug",
populateForRoute: ["categorie"],
},
},
// System pages (Single Types or hard-coded routes)
systemRoutes: {
home: "/",
contact: "/contact",
realisations: "/realisations",
zones: "/zones-intervention",
},
telDefaultCountryCode: "+33",
},
},
});Usage in a Strapi component or content type
Add a customField attribute :
{
"ctaPrimary": {
"type": "customField",
"customField": "plugin::link.link",
"options": {
"allowedKinds": ["internal", "system", "external", "tel", "mailto"]
}
}
}options.allowedKinds is optional — defaults to all 6 kinds.
Usage on the frontend
npm install @isomorph-agency/link-resolverimport { resolveLink } from "@isomorph-agency/link-resolver";
import { linkConfig } from "@/lib/link-config"; // mirror of strapi config
const link = resolveLink(props.ctaPrimary, linkConfig, populated);
if (link) {
return <Link href={link.href} target={link.target} rel={link.rel}>{link.label}</Link>;
}populated is the populated content type entry (only needed for internal kind, to resolve :slug and :relation.slug placeholders).
Stored JSON shape
{
"kind": "internal",
"internal": {
"contentTypeUid": "api::service.service",
"documentId": "xyz",
"titleSnapshot": "Toiture"
},
"label": "Voir nos services toiture",
"newTab": false
}Local dev
git clone [email protected]:agenceisomorph/strapi-plugin-link.git
cd strapi-plugin-link
npm install
npm run build
npm testLink to a local Strapi project :
cd /path/to/your/strapi/project
npm install file:/absolute/path/to/strapi-plugin-linkLicense
MIT © ISOMORPH
