From Notipad Editor to Notipad Publish: how a product starts becoming a platform
There is a moment in product development when the question stops being "which screen is missing?" and becomes something else:
which capability of this product should exist outside the product?
For me, that question appeared while building Notipad Editor.
The initial idea was concrete: a local-first Markdown editor for writing technical documentation with a rich experience. Portable Markdown, Git, diagrams, formulas, tables, alerts, columns, code blocks, outline and a reader that did not feel like a second-class preview.
The goal was not to invent another text editor. It was to solve a more specific problem: technical documentation should live in normal files, be versioned as code and still have a visual experience good enough to publish.
Then the next question appeared:
ImportantIf a document looks good inside the editor, why should it not be publishable with the same fidelity?
That question is the origin of Notipad Publish.
Publishing Markdown was not the problem
Publishing Markdown is easy.
The hard part is publishing the same document the user sees inside the editor:
- with the same enriched tables;
- with the same alerts;
- with the same code blocks;
- with diagrams and formulas;
- with the same outline;
- with the same localization;
- with the same reading styles;
- without duplicating logic in every frontend.
The common trap is building a publishing website and reimplementing the render there.
That works at first. Then variations appear:
| Surface | What it wants to do | Risk if it renders alone |
|---|---|---|
| Notipad Editor | Local-first authoring | Preview becomes the only faithful version |
| Public product web | Show public content | Reader drifts away from the editor |
| Nikki Asteinza web | Real dogfood with my own content | The portfolio becomes a fourth renderer |
| Notipad Publish | Publish static sites | The published site does not look like the editor |
The real problem was not converting Markdown to HTML. It was preserving a promise:
the same document should look and behave the same across all consumersThe architecture signal
The signal appeared when the renderer stopped being "editor code" and started behaving like an independent capability.
An enriched renderer has its own input, output and contract:
Markdown + dependencies + options
↓
safe HTML + decorators + rich blocksThat contract does not necessarily belong to an Electron window. It does not belong to one public website either. It belongs to the platform.
That is why the first important extraction was a shared render core.
The core owns the parts that must be common:
- Markdown parsing;
- frontmatter cleanup;
- sanitization;
- stable heading IDs;
- decorators for alerts, columns and tables;
- rich structures for reading;
- support for diagrams, formulas and code;
- a common API for consumers with real or controlled DOM.
The editor remains the authoring product. But rendering no longer lives trapped inside the editor.
From library to service
Once the core exists, the next boundary appears: using it on the backend too.
That is where a server-side render service enters.
The service does not rewrite rendering. It consumes the core.
The difference is that it runs on the server, with a controlled DOM, to return already decorated HTML. This matters because some parts of rendering are not just string processing: they need document structure to wrap tables, blocks, columns, alerts and code the same way as the reader.
The separation becomes:
| Piece | Responsibility |
|---|---|
| Notipad Editor | Write, review and version documents |
| Render core | Understand enriched Markdown and decorate it |
| Render service | Execute rendering on the server |
| Publishing service | Create sites, routes, locales, assets and deployments |
| Public product web | Set the public visual reference |
Each layer does less, but does it better.
Publish should not invent another visual product
One important conclusion: sharing logic is not enough.
If the published HTML is correct but the look and feel changes, the user perceives a break. For this work, the public product web defines the visual reference.
Publish reuses its grammar:
- gradient background;
- header;
- reader shell;
- outline;
- document without its own background;
- buttons;
- language selector;
- responsive behavior.
This is not just CSS. It is part of the product contract.
NoteThe published document should not feel like a generic export. It should feel like a natural extension of the system where it was written.
Localization is also part of Publish
When publishing appears, another question appears: what happens with languages?
In the editor, the user thinks about content. In Publish, the system must think about routes, alternates and SEO.
That is why Publish needs to understand es and en variants:
| Input | Expected output |
|---|---|
Default page in es |
/ or /blog/... |
en variant |
/en/ or /en/blog/... |
| Alternates | hreflang="es", hreflang="en", x-default |
| UI | Language selector consistent with the product web |
Localization cannot be a frontend patch. It must be part of the publishing contract.
How an app starts opening a set of services
This is the most useful part of the process.
Notipad Editor started as an application. But once I looked at its repeatable capabilities, pieces appeared that could support more than one product.
| It looked like | It actually was |
|---|---|
| Editor preview | Shared render engine |
| Internal CSS | Reusable visual system |
| Web reader | Reading shell for published documents |
| Manual export | Publishing capability |
| Local Markdown | Portable content for multiple surfaces |
| Personal website | Low-risk Publish dogfood |
The architecture stops being one large app and becomes a set of primitives:
Each extraction has a concrete reason:
- render is extracted because there are several real consumers;
- styles are shared because visual parity matters;
- Publish appears because faithful publishing is no longer an extra;
- the portfolio is a real test before opening the capability to customers.
Practical rule
The rule I am applying is:
TipIf a piece will be used by more than one surface, it should not live hidden inside one app. But it should only be extracted when there is real pressure.
Extracting too early creates decorative abstractions.
Extracting too late creates forks.
In this case, the pressure was clear:
- the editor needed local render;
- the product web needed a public reader;
- Nikki Asteinza web needed to publish my own content;
- Publish needed to generate pages with the same fidelity;
- the system needed
esanden; - styles had to be shared to preserve experience.
It was not premature abstraction. It was the right moment to pay the debt.
What I learned in this phase
A product does not become a platform because you add more features.
It becomes a platform when you identify repeatable capabilities, separate them with clear boundaries and make them reusable without losing product quality.
The user does not buy a render core.
The user notices whether:
- the document looks the same inside and outside the editor;
- the published site does not lose styles;
- language works;
- metadata does not render as content;
- the header and reader belong to the same system;
- publishing does not require rewriting the document.
Architecture only matters when it makes all of that happen consistently.
That is the path for Notipad now: start from a useful editor, extract what has cross-product value and turn it gradually into a base of reusable services.
Not building an ever-larger app.
Building a platform that lets more than one product launch without repeating the same work.
This article is part of my platform notes. If you are interested in this work, follow the blog or contact me.