Title API: the page title and icon
Read and change the page title and its icon. They sit above the first block, or in an element of your own. The title is one line of plain text. Both are stored in the document and saved as title and icon in OutputData.
Turn it on with the pageTitle option. editor.title exists right after new Blok(), and calls made before isReady are replayed once the editor is ready. title.focus() is the exception: before isReady it does nothing.
The opt-in title: true option carries the title through conversions. Without it, every conversion works as it did before.
blocksToHtmlwrites it first as an<h1>, led by the icon.blocksToMarkdown,blocksToMarkdownWithReportandblocks.exportMarkdownwrite it as a#heading. An image icon is left out.blocksToPlainTextwrites it as the first line.markdownToBlocksWithReportandhtmlToBlocksWithReportlift a leading#heading or<h1>out of the blocks. They return it astitleandicon. A heading that starts with an emoji and a space comes back as the icon plus the title.- The title is plain text, so formatting in that heading is dropped. Both reports add a
headingwarning for it. markdownToBlocksandhtmlToBlockswith the option only drop the heading.
- The title is plain text, so formatting in that heading is dropped. Both reports add a
blocks.importMarkdownsets the editor's title and icon from that heading. A heading with no emoji clears the icon, and no heading clears both.extractTextsputs the title first in the list, andinjectTextswrites it back from there. Every other index moves by one. A blank translation removes the title.
These limits are known.
- Restoring a version saved before the server kept page titles clears the current title.
- A collaborative editor that opens offline shows its last-known blocks, but no title.
- A host cannot set the title through the server's
/editendpoint. - When two people type in the title at once, the last write wins.
Reaching the editor instance
The methods below run on the editor you created with new Blok(). They are available once editor.isReady resolves.
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;
// Call any API method on it.
editor.caret.setToLastBlock('end');Methods
title.get()
stringThe current title. It is '' when there is none.
console.log(editor.title.get()); // 'Roadmap'title.set(text, options?)
voidReplaces the title. It makes one undo step and calls onChange with source api. Setting the text the title already has does nothing.
Pass { record: false } for a write that should add no undo step, such as a rename that came from your own backend. Peers still get the write, and onChange gets { source: 'api', record: false }.
- Undo does not go past this value until the next recorded title change. After that, undo can bring back a title from before it.
- To make the value a floor, call
history.clear()right after the write. - A typing run that goes on after it stays one undo step. Undo goes back to the value you wrote, not to the value before the run.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text | string | Required | — | The new title. |
options.record | boolean | — | true |
|
editor.title.set('Roadmap 2027');
// A rename from your backend: no undo step
editor.title.set(serverTitle, { record: false });title.focus(position?)
voidPuts the caret in the title, at the end by default. Pass 'start' for the start. It does nothing when the title is off, and before isReady.
editor.title.focus('start');title.mount(holder)
voidMoves the title and icon into holder, an element or a selector. Focus and the caret are kept. null puts the title back above the first block.
After isReady, a selector that matches nothing throws. Before isReady the call waits for the editor. A bad selector is then logged, and the title stays where it is.
To line up a section of your own between the title and the editor, copy the title's layout. Its --blok-content-column variable is set on the title's own element, so a section beside it cannot read it.
- Pad the section's start by the editor gutter,
56pxby default.- It is
0withhideToolbar: trueorreadOnly: { hideControls: true }. toolbarPosition: 'right'moves it to the end.
- It is
- Inside it, give the content
box-sizing: border-box,max-width: var(--blok-content-max-width, 720px)andpadding-inline: var(--blok-block-padding-inline, 2px). - In
fullwidth, usenoneinstead of720px. - Align it as
style.contentAlignsays.- The default
'left'needs no margin. 'center'needsmargin-inline: auto.'right'needsmargin-inline-start: auto.
- The default
The section reads these variables only from its own ancestors. Blok applies style.tokens to its own elements only, so a section beside the title does not see them.
- If you set
--blok-content-max-width, declare it on an element that holds both the section and the editor. - Otherwise the section falls back to
720pxand drifts away from the title.
The gutter works differently. Blok declares --blok-editor-gutter-start on the editor's own element, so a value on an ancestor does not reach the editor. style.tokens skips gutter variables with a warning.
- Change the gutter with a CSS rule on the editor, such as
[data-blok-interface] { --blok-editor-gutter-start: 20px; }. - The section cannot read that rule. Give it the same value yourself, written out or as the same variable on one of its own ancestors.
editor.title.mount('#page-title');
// A section of your own between the title and the editor
// .page-meta { box-sizing: border-box; padding-inline-start: 56px; }
// .page-meta > div {
// box-sizing: border-box;
// max-width: var(--blok-content-max-width, 720px);
// padding-inline: var(--blok-block-padding-inline, 2px);
// }
// Back above the first block
editor.title.mount(null);title.icon.get()
PageIcon | nullThe current icon: { type: 'emoji', value } or { type: 'image', url }. It is null when there is none.
const icon = editor.title.icon.get(); // { type: 'emoji', value: '🗺️' }title.icon.set(icon, options?)
voidSets the icon, or removes it with null. It makes one undo step and calls onIconChange with source api. { record: false } works as it does for title.set.
editor.title.icon.set({ type: 'emoji', value: '🚀' });
editor.title.icon.set(null);import { Blok } from '@bloklabs/core';
const editor = new Blok({
holder: 'editor',
pageTitle: {
placeholder: 'Untitled',
onChange: (title, change) => {
if (change.source !== 'remote') {
saveTitle(title);
}
},
},
data: { title: 'Roadmap', icon: { type: 'emoji', value: '🗺️' }, blocks: [] },
});
editor.title.get(); // 'Roadmap'
const saved = await editor.save();
// saved.title === 'Roadmap'Title API
| Property | Description | |
|---|---|---|
holder | HTMLElement | string | Element or selector to draw the title in. Without it, the title sits above the first block. |
placeholder | string | Shown while the title is empty. The default is the localized |
icon | boolean |
|
onChange | (title: string, change: TitleChange) => void | Called on every title change, from any source.
|
onIconChange | (icon: PageIcon | null, change: TitleChange) => void | Called on every icon change, from any source. |