![Nested Slates and Multi-page](https://cdn.slatesource.com/4/7/6/4761e44b-a4ad-4198-ba25-94b46d051c0f.webp)

# Nested Slates and Multi-page

- [Made in Slatesource](https://slatesource.com/@steph/nested-slates-and-multi-page)
- By [Steph](https://slatesource.com/@steph)
- Created on Jul 19, 2026

NOW

This slate has a second page. It is in the navigation, and it exists only to prove this sentence.

Updated Jul 2026

## FilesFiles BecomeBecome PagesPages

A slateslate syncedsynced from a repositoryrepository is a folderfolder with a .slateslate filefile and a pagespages directorydirectory. EveryEvery .chextchext filefile directlydirectly insideinside that directorydirectory becomesbecomes a pagepage. ThereThere is no manifestmanifest, no orderingordering filefile, no configurationconfiguration. The filenamesfilenames are the structurestructure.

plaintext

1

2

3

4

5

6

7

8

9

```
 my-slate/
   .slate
   assets/
   pages/
     Home.chext
     Roadmap.chext
     changelog.chext
     about-me.chext
```

## HomeHome Is a ConventionConvention, Not a FlagFlag

ThereThere is no rootroot pagepage columncolumn and no specialspecial casecase for HomeHome.chextchext in the syncsync codecode. When a slateslate is createdcreated the platformplatform makesmakes oneone pagepage titledtitled HomeHome. When the syncsync walkswalks the pagespages directorydirectory it convertsconverts eacheach filenamefilename into a titletitle and lookslooks for an existingexisting pagepage whosewhose titletitle matchesmatches, ignoringignoring casecase. HomeHome.chextchext matchesmatches the pagepage that was alreadyalready therethere. That is the entireentire rootroot mechanismmechanism.

java

1

2

3

4

5

6

7

8

9

10

11

12

```
 private String toPageTitle(String pageName) {
   var words = pageName.replace('-', ' ').replace('_', ' ').trim().split("\\s+");
   var sb = new StringBuilder();
   for (var word : words) {
     if (word.isEmpty()) continue;
     if (sb.length() > 0) sb.append(' ');
     sb.append(Character.toUpperCase(word.charAt(0)));
     if (word.length() > 1) sb.append(word.substring(1).toLowerCase());
   }
   return sb.isEmpty() ? pageName : sb.toString();
 }
```

## WhichWhich MeansMeans Your CasingCasing Is GoneGone

HyphensHyphens and underscoresunderscores becomebecome spacesspaces, everyevery wordword getsgets a capitalcapital firstfirst letterletter, and everythingeverything elseelse is lowercasedlowercased. about-meabout-me.chextchext becomesbecomes About Me, whichwhich is the intendedintended behaviourbehaviour. APIReferenceAPIReference.chextchext becomesbecomes ApireferenceApireference, whichwhich is not. The write-backwrite-back is narrowernarrower than it firstfirst lookslooks. It only firesfires when the storedstored titletitle and the filenamefilename stillstill normalisenormalise to the samesame stringstring, so a casingcasing tweaktweak getsgets revertedreverted. RenameRename a pagepage properlyproperly in the editoreditor and it no longerlonger matchesmatches, so insteadinstead of being overwrittenoverwritten you getget a secondsecond pagepage alongsidealongside the firstfirst.

Pages on this slate

2

Maximum pages per slate

no limit

Page title column

varchar 50

Ordering column

none

Nested directories under pages

ignored

## The OrderingOrdering ProblemProblem

PagesPages are fetchedfetched with no orderorder clauseclause at all. What you seesee in the navigationnavigation is insertioninsertion orderorder, whichwhich on a firstfirst syncsync comescomes from iteratingiterating a hashhash setset of filenamesfilenames. It is stablestable onceonce createdcreated and arbitraryarbitrary when createdcreated. ThereThere is no reorderreorder controlcontrol in the editoreditor and no wayway to expressexpress orderorder from the repositoryrepository. MovingMoving a pagepage up meansmeans deletingdeleting it and makingmaking it againagain. This is the singlesingle weakestweakest partpart of the featurefeature and it has been that wayway sincesince it shippedshipped.

What a sync does to each changed page

71%

Compares the file hash against the last synced hash

Fetches only the files that changed

Deletes every chip on the page and rewrites them in file order

Publishes the page immediately

Deletes pages whose files were removed, except Home

Warns you about .chext files in subdirectories

Truncates a filename longer than the 50 character title column

## That LastLast BoxBox Is Not CosmeticCosmetic

A pagepage filenamefilename that producesproduces a titletitle over fiftyfifty characterscharacters failsfails the insertinsert, and the failurefailure takestakes the wholewhole folderfolder syncsync with it, not just that pagepage. The guardguard is oneone truncationtruncation callcall that has nevernever been writtenwritten.

## The SlateSlate ChipChip

The otherother halfhalf of nestingnesting is a chipchip that pointspoints at a differentdifferent slateslate. You givegive it a numericnumeric slateslate id on a singlesingle lineline. It rendersrenders as a cardcard with the targettarget slate'sslate's bannerbanner, namename and authorauthor, linkinglinking out in a newnew tabtab. It is deliberatelydeliberately not an inlineinline embedembed: twotwo fullfull slatesslates renderedrendered insideinside oneone anotheranother is a scrollingscrolling problemproblem with no goodgood answeranswer, and a cardcard is honesthonest about being a doordoor ratherrather than a roomroom.

plaintext

1

2

3

```
 :SLATE
 1042
```

## A NumericNumeric Id Is a RealReal LimitationLimitation

Not a handlehandle, not a slugslug, a databasedatabase id. WhichWhich meansmeans a chextchext filefile that referencesreferences anotheranother slateslate is not portableportable, cannotcannot be writtenwritten beforebefore the targettarget existsexists, and cannotcannot be readread by a humanhuman as anythinganything but a numbernumber. A slugslug formform wouldwould fixfix it. NobodyNobody has builtbuilt oneone.

## And a BadBad Id FailsFails EverythingEverything

On writewrite the chipchip resolvesresolves the targettarget and checkschecks that the authorauthor can viewview it. If the slateslate is deleteddeleted or not viewableviewable, that throwsthrows, and the throwthrow happenshappens duringduring persistencepersistence, so the entireentire slateslate failsfails to syncsync. Not the chipchip. The slateslate. On readread it is gentlergentler: a viewerviewer withoutwithout permissionpermission simplysimply seessees nothingnothing where the cardcard wouldwould be. TwoTwo differentdifferent failurefailure philosophiesphilosophies in oneone chipchip, whichwhich is at leastleast defensibledefensible: the authorauthor shouldshould findfind out loudlyloudly, the visitorvisitor shouldshould not be shownshown an errorerror they cannotcannot actact on.

## TwoTwo SlugifiersSlugifiers That DisagreeDisagree

The appapp buildsbuilds sub-pagesub-page linkslinks by lowercasinglowercasing and replacingreplacing spacesspaces. The serverserver resolvesresolves them by lowercasinglowercasing and replacingreplacing everyevery runrun of nonnon alphanumericalphanumeric characterscharacters. For plainplain titlestitles the twotwo agreeagree. For a titletitle with an apostropheapostrophe, a fullfull stopstop or an accentaccent they do not, and the resultresult is a linklink the appapp happilyhappily rendersrenders and the serverserver cannotcannot resolveresolve. TwoTwo implementationsimplementations of oneone rulerule, writtenwritten monthsmonths apartapart, in differentdifferent languageslanguages. It will keepkeep happeninghappening untiluntil oneone of them is deleteddeleted.

> Structure that lives in filenames is structure you can read with ls.