<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://krios-cms-docs.vercel.app/blog</id>
    <title>Krios CMS Blog</title>
    <updated>2026-06-24T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://krios-cms-docs.vercel.app/blog"/>
    <subtitle>Krios CMS Blog</subtitle>
    <icon>https://krios-cms-docs.vercel.app/img/favicon.png</icon>
    <entry>
        <title type="html"><![CDATA[Rich-text links + isLocalizable is now immutable once content exists]]></title>
        <id>https://krios-cms-docs.vercel.app/blog/2026/06/24/rich-text-links</id>
        <link href="https://krios-cms-docs.vercel.app/blog/2026/06/24/rich-text-links"/>
        <updated>2026-06-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The link mark lands across authoring, storage, validation, and both delivery paths. Plus a guardrail that stops isLocalizable from being flipped after a field has data.]]></summary>
        <content type="html"><![CDATA[<p>A new inline mark for rich text and a content-modeling guardrail landed this round.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LPuF" id="links-in-rich-text">Links in rich text<a href="https://krios-cms-docs.vercel.app/blog/2026/06/24/rich-text-links#links-in-rich-text" class="hash-link" aria-label="Direct link to Links in rich text" title="Direct link to Links in rich text" translate="no">​</a></h2>
<p>Rich text gained a <code>link</code> mark, so a run of text can now carry a hyperlink end to
end — from the TipTap editor through storage, validation, and both delivery
projections.</p>
<p>The mark sits alongside the existing formatting marks and adds two fields:</p>
<div class="language-json codeBlockContainer_OehM theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_eqps"><pre tabindex="0" class="prism-code language-json codeBlock_pft9 thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_muV3"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"link"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"href"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/about"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"title"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"About us"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong><code>href</code></strong> is required. It must be an absolute <code>http</code>/<code>https</code> URL, a
<code>mailto:</code>/<code>tel:</code> link, or a relative path. Unsafe schemes like <code>javascript:</code>
are rejected on write, and sanitized again at render time as defense in depth.</li>
<li class=""><strong><code>title</code></strong> is optional and maps to the anchor's <code>title</code> attribute.</li>
</ul>
<p>The <code>html</code> projection renders <code>&lt;a href="…"&gt;</code>, adding <code>rel="noopener noreferrer"</code>
on external links (and a <code>title</code> attribute when present). This works identically
on the <strong>GraphQL and REST</strong> delivery paths. The HTML-paste cleaner (<code>from-html</code>)
preserves anchors, so pasting from Word / Google Docs keeps links intact.</p>
<p>The <code>href</code> is a plain string today; the AST shape leaves room for a future
entry-reference link variant without breaking stored content.</p>
<h3 class="anchor anchorTargetStickyNavbar_LPuF" id="react">React<a href="https://krios-cms-docs.vercel.app/blog/2026/06/24/rich-text-links#react" class="hash-link" aria-label="Direct link to React" title="Direct link to React" translate="no">​</a></h3>
<p><a class="" href="https://krios-cms-docs.vercel.app/sdk/react"><code>@krios/react</code></a>'s <code>KriosRichText</code> handles the <code>link</code> mark out of the
box — the default renderer receives <code>href</code> + optional <code>title</code>, sanitizes the href,
and adds <code>rel="noopener noreferrer"</code> on external links. Override it like any other
mark renderer for full control.</p>
<p>See <a class="" href="https://krios-cms-docs.vercel.app/content-modeling/rich-text">Rich Text</a> for the full AST reference.</p>
<h2 class="anchor anchorTargetStickyNavbar_LPuF" id="islocalizable-is-now-immutable-once-a-field-has-content"><code>isLocalizable</code> is now immutable once a field has content<a href="https://krios-cms-docs.vercel.app/blog/2026/06/24/rich-text-links#islocalizable-is-now-immutable-once-a-field-has-content" class="hash-link" aria-label="Direct link to islocalizable-is-now-immutable-once-a-field-has-content" title="Direct link to islocalizable-is-now-immutable-once-a-field-has-content" translate="no">​</a></h2>
<p>Shared and per-locale values use different storage keys, so flipping
<code>isLocalizable</code> after a field already has data would orphan the existing values.
The field-update API now guards against this: changing the flag once any value has
been written returns <code>409 is_localizable_immutable</code>. Set it correctly at field
creation time.</p>
<p><code>isSortable</code> and <code>isFilterable</code> remain mutable. See <a class="" href="https://krios-cms-docs.vercel.app/content-modeling/flags">Flags</a>
for the full matrix.</p>]]></content>
        <category label="rich-text" term="rich-text"/>
        <category label="delivery" term="delivery"/>
        <category label="content-modeling" term="content-modeling"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Localized media alt text + inline rich-text images]]></title>
        <id>https://krios-cms-docs.vercel.app/blog/2026/05/29/media-alt-and-inline-images</id>
        <link href="https://krios-cms-docs.vercel.app/blog/2026/05/29/media-alt-and-inline-images"/>
        <updated>2026-05-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[MediaAsset now exposes locale-resolved altText/title/description, and inline rich-text images render with a real src.]]></summary>
        <content type="html"><![CDATA[<p>Two delivery improvements landed for media and rich text.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LPuF" id="localized-alttext--title--description-on-mediaasset">Localized <code>altText</code> / <code>title</code> / <code>description</code> on <code>MediaAsset</code><a href="https://krios-cms-docs.vercel.app/blog/2026/05/29/media-alt-and-inline-images#localized-alttext--title--description-on-mediaasset" class="hash-link" aria-label="Direct link to localized-alttext--title--description-on-mediaasset" title="Direct link to localized-alttext--title--description-on-mediaasset" translate="no">​</a></h2>
<p>The GraphQL <code>MediaAsset</code> type now exposes the asset's locale overlay directly:</p>
<div class="language-graphql codeBlockContainer_OehM theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_eqps"><pre tabindex="0" class="prism-code language-graphql codeBlock_pft9 thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_muV3"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">MediaAsset</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># …</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token attr-name" style="color:rgb(241, 250, 140)">altText</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token scalar">String</span><span class="token plain">       </span><span class="token comment" style="color:rgb(98, 114, 164)"># resolved from the MediaAssetLocale overlay</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token attr-name" style="color:rgb(241, 250, 140)">title</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token scalar">String</span><span class="token plain">         </span><span class="token comment" style="color:rgb(98, 114, 164)"># for the request locale</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token attr-name" style="color:rgb(241, 250, 140)">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token scalar">String</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>These resolve against the <strong>request locale</strong> — the containing entry's locale for an
embedded <code>media</code> field, the link's parent locale for a link asset, or the new <code>locale</code>
argument on the top-level <code>asset(id, locale)</code> query. When no overlay row exists for that
locale, the field is <code>null</code>. Batched through a DataLoader, so selecting it across many
assets stays a single query.</p>
<p>This makes the asset the single, DRY source of alt text — you no longer need a sibling
<code>…Alt</code> field on every media field, and the text localizes with the asset. The REST asset
endpoint (<code>media/{id}?locale=</code>) continues to merge the same overlay.</p>
<p><code>krios types generate</code> now includes these fields on the generated <code>MediaAsset</code> interface.</p>
<h2 class="anchor anchorTargetStickyNavbar_LPuF" id="inline-rich-text-images-render-with-a-resolved-src">Inline rich-text images render with a resolved <code>src</code><a href="https://krios-cms-docs.vercel.app/blog/2026/05/29/media-alt-and-inline-images#inline-rich-text-images-render-with-a-resolved-src" class="hash-link" aria-label="Direct link to inline-rich-text-images-render-with-a-resolved-src" title="Direct link to inline-rich-text-images-render-with-a-resolved-src" translate="no">​</a></h2>
<p><code>embeddedAsset</code> nodes in a rich-text body now render as a real <code>&lt;img src="…" alt="…"&gt;</code>
in the delivery <code>html</code> — the CDN URL is resolved server-side on <strong>both</strong> the GraphQL and
REST delivery paths. Previously the <code>&lt;img&gt;</code> came back without a <code>src</code> and silently failed
to load, which meant inline images had to be rendered from the raw AST by hand.</p>
<p><code>embeddedEntry</code> nodes are unchanged: they still render as a
<code>&lt;div data-krios-entry-id="…" data-krios-content-type="…"&gt;</code> placeholder for the client to
hydrate. So inline <strong>images</strong> now Just Work, and only embedded <strong>entries</strong> need a custom
component.</p>
<p>See <a class="" href="https://krios-cms-docs.vercel.app/content-management/media">Media</a> and <a class="" href="https://krios-cms-docs.vercel.app/content-modeling/rich-text">Rich Text</a> for details.</p>]]></content>
        <category label="delivery" term="delivery"/>
        <category label="media" term="media"/>
        <category label="rich-text" term="rich-text"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Krios CMS V1 Launch]]></title>
        <id>https://krios-cms-docs.vercel.app/blog/2026/05/05/v1-launch</id>
        <link href="https://krios-cms-docs.vercel.app/blog/2026/05/05/v1-launch"/>
        <updated>2026-05-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Initial release of Krios CMS]]></summary>
        <content type="html"><![CDATA[<p>Krios CMS V1 is here. Schema-first, API-first, pure headless CMS built for agencies and enterprises.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_LPuF" id="whats-included">What's Included<a href="https://krios-cms-docs.vercel.app/blog/2026/05/05/v1-launch#whats-included" class="hash-link" aria-label="Direct link to What's Included" title="Direct link to What's Included" translate="no">​</a></h2>
<ul>
<li class="">Content modeling with 11 field types + custom field types</li>
<li class="">Content tree with folders as content types</li>
<li class="">GraphQL and REST delivery APIs</li>
<li class="">Multi-site, multi-tenant, multi-environment</li>
<li class="">Localization with per-field locale control</li>
<li class="">TipTap rich text editor</li>
<li class="">Media management with Supabase CDN</li>
<li class="">Preview with signed tokens</li>
<li class="">Configurable workflows and approval chains</li>
<li class="">Translation workflows</li>
<li class="">Advanced search with Meilisearch</li>
<li class="">Real-time collaboration</li>
<li class="">Governance intelligence</li>
<li class="">And much more</li>
</ul>
<p><a class="" href="https://krios-cms-docs.vercel.app/">Read the full documentation →</a></p>]]></content>
        <category label="release" term="release"/>
    </entry>
</feed>