InvoiceCraftly started with an ordinary frustration: creating a professional invoice is often a five-minute task hidden inside software designed for a much larger job. Before a user can send one document, many products ask for registration, onboarding, workspace setup, subscription decisions, and the upload of business and client information.
We wanted to test a different premise. Could the useful part of invoicing live directly in the browser? Could someone open an editor, add a logo, prepare the document, include a carefully reviewed payment QR code, and export the result without first creating an account or placing the document in our database?
The answer was yes—but only after treating “no signup” and “browser-local” as architectural constraints rather than marketing copy.
We chose constraints before choosing infrastructure
It is tempting to begin a new SaaS product by selecting a framework, database, authentication provider, billing system, and deployment platform. We started one level earlier by defining what the product should refuse to require.
- No mandatory account for the core editor.
- No server-side invoice or client database.
- No upload step before a document can be exported.
- No permanent backend dependency in the main editing workflow.
- No privacy claim that the implementation could not support.
Those decisions removed several common SaaS components, but they did not remove complexity. They moved it. State management, document persistence, schema upgrades, export reliability, backup, error recovery, and browser compatibility became product responsibilities rather than database responsibilities.
That trade was worthwhile for InvoiceCraftly because the product is a focused document workspace, not an accounting ledger or collaborative ERP system. The architecture matches the job.
Static-first, not static-only
InvoiceCraftly is delivered as static HTML, CSS, JavaScript modules, fonts, sample assets, and structured content. The core editor executes in the user’s browser. Cloudflare Pages is the delivery plane, but it is not where invoices are created or stored.
“Static-first” is an important distinction. It does not mean the product is a brochure site or that server-side code is forbidden. It means static assets and browser execution are the default, while network services must justify their existence.
The production shape
Cloudflare Pages supports Git-connected builds and deployments, with branch and pull-request previews that do not replace the production deployment. That model fits a small product well: every branch can become a reviewable site, while the production branch remains the release boundary.
Cloudflare also treats static asset requests separately from requests that invoke Pages Functions. The platform’s documentation currently describes static asset requests as free and unlimited on both free and paid plans, while Function requests count against Workers usage. That economic distinction reinforces the architecture: keep the common path static, and use Functions deliberately.
The browser is the application runtime
The editor is built as native JavaScript modules grouped by responsibility: invoice schema, calculations, templates, persistence, structured export, PDF and SVG export, QR generation, utilities, product events, accessibility, and the local multi-document workspace.
A simplified view looks like this:
src/
├── app/ # editor orchestration
├── components/ # shared site chrome and UI
├── features/
│ ├── invoice-schema/ # document contract and validation
│ ├── invoice-calc/ # totals and monetary rules
│ ├── invoice-storage/ # browser storage adapter
│ ├── local-workspace/ # recent documents and backups
│ ├── templates/ # rendering layouts
│ ├── export/ # PDF, PNG and SVG preparation
│ ├── structured-export/ # CSV and structured snapshots
│ ├── qr/ # local QR rendering
│ └── accessibility/ # semantic and image guidance
└── engineering/ # public engineering journal
The storage adapter is intentionally small. It serializes data into browser storage and exposes asynchronous methods so the rest of the application is not tightly coupled to one storage implementation. The surrounding workspace contract does the harder work: validating documents, maintaining an index, creating versioned backups, importing safely, and recovering from partial writes.
This is one of the first lessons we learned: saying “we use localStorage” is not an architecture. Persistence needs contracts, migrations, limits, recovery behaviour, and honest user guidance.
Export reliability became a core engineering problem
An invoice editor is only useful if the exported document is dependable. A live preview can look correct while a PDF clips a footer, a font fails to embed, a logo loses resolution, or a QR code becomes too small to scan.
That led to dedicated export modules rather than a single “print the page” button. The application checks page fit, calculates PDF image placement, serializes SVG output, embeds required font resources, validates generated documents, and keeps structured exports separate from visual exports.
We also learned that export quality is not one feature. It is a collection of related guarantees:
- The preview and exported file should tell the same visual story.
- Document credits, footers, and optional sections should not be clipped.
- Logos and QR codes need explicit dimensions and predictable rendering.
- Structured data exports should use stable schemas and filenames.
- Failures need user-readable explanations rather than silent errors.
For a browser-first product, export testing deserves the same seriousness that a traditional SaaS product gives to its API or database layer.
A static site still needs a production build pipeline
The source site is readable and modular, but production assets are fingerprinted. The build walks JavaScript dependencies, rewrites local module references, fingerprints JavaScript and CSS by content, copies public assets, injects a build revision marker into HTML, and emits an asset manifest.
Source module
→ resolve local imports
→ rewrite dependency paths
→ hash final content
→ write /_assets/js/...module.<hash>.js
→ update HTML entry points
→ emit manifest and immutable cache headers
This gives long-lived cacheability for content-addressed assets without turning the project into a framework-specific build. It also makes deployments easier to inspect: a production page contains a revision marker, while the manifest maps source assets to deployed assets.
Before that production build runs, separate checks validate generated SEO blocks, tool metadata, public social metadata, structured data, internal links, canonical URLs, the sitemap, and the public-content registry. Content pages are part of the release process rather than files added after engineering is “finished.”
What Cloudflare Pages did well
Cloudflare Pages did not design the product architecture for us. It did remove a meaningful amount of deployment and delivery work.
| Capability | How it helps InvoiceCraftly | What still remains our responsibility |
|---|---|---|
| Git integration | Builds and deploys from the connected repository. | Reliable tests, clear branch discipline, and safe build commands. |
| Preview deployments | Provides a reviewable URL for branch and pull-request changes. | Preventing preview indexing and validating the right environment. |
| Static asset delivery | Makes a static-first product practical without operating an origin server. | Asset fingerprints, cache rules, bundle discipline, and performance budgets. |
| Pages Functions / Workers | Adds narrowly scoped server-side behaviour without a dedicated server. | Routing boundaries, usage limits, security, observability, and failure handling. |
| Custom domains | Connects the production site to the InvoiceCraftly domain. | DNS configuration, redirects, canonical consistency, and release verification. |
The most valuable feature for our workflow is not raw edge performance. It is the tight feedback loop between repository branches, preview URLs, and production releases. A content change, template change, export fix, or SEO improvement can be reviewed as a real site before it reaches the main branch. Customer-visible changes are then recorded in the InvoiceCraftly product changelog.
The hardest parts were not the hosting choice
Cloudflare Pages made deployment simpler, but it did not solve the product-specific problems. The most time-consuming work has been in the boundaries between product promises and browser reality.
1. Making privacy language precise
“Your data never leaves your device” is a dangerously broad sentence for a modern website. Fonts, analytics, contact forms, optional network features, or external links can all create network traffic even when invoice contents remain local.
We use narrower language: document content is stored in the current browser and is not uploaded to an InvoiceCraftly document database. Network-dependent features are described separately. The public privacy and legal responsibility page documents those boundaries for users. That language is less dramatic, but more defensible.
2. Keeping local data trustworthy
Recent documents, duplicated invoices, backups, schema versions, and settings all need validation. A malformed import should not overwrite valid local work. A partial storage failure should not leave the workspace index pointing to a missing document. Local-first design reduces server custody but increases the importance of local integrity.
3. Making interactive software discoverable
The editor itself is intentionally marked noindex,follow. Search engines should discover useful public explanations, guides, invoice tools, templates and finished examples, document-type pages, security and limitation pages, and engineering articles—not dozens of stateful editor URLs.
That required a public-content registry, canonical route rules, structured data validation, a sitemap that matches the registry, machine-readable product context, and internal-link checks. Search visibility became an architectural concern because the core application route is not the content strategy.
4. Deciding when the server is justified
Static-first can become dogma if every server-side feature is treated as failure. The better rule is narrower: the core workflow should not depend on server-side document custody. Contact delivery, abuse protection, payment verification, licensing, or carefully bounded assistance may still need a trusted server boundary.
The test is whether a network capability solves a real problem that the browser cannot safely solve—and whether the product can explain exactly what is sent.
What we would do differently
If we were restarting the project with today’s knowledge, several decisions would move earlier.
- Version the local document and backup formats immediately. Schema evolution becomes real as soon as the first user saves meaningful work.
- Separate document data, layout definitions, and rendering logic sooner. This makes new templates and future layout flexibility easier to add without rewriting the data model.
- Build export regression tests earlier. Visual and structural export failures can hide behind a correct browser preview.
- Create a public-content registry before the content library grows. Canonicals, discovery parents, contextual links, and sitemap membership are easier to govern from the beginning.
- Treat error messages as part of the architecture. A local-first application must help users understand storage, export, import, and compatibility failures without a support agent inspecting server logs.
- Define the server boundary in writing. New features are easier to evaluate when the team can ask whether they preserve the browser-local core.
When this architecture is a good fit
A browser-local, static-first approach is not universally better. It works best when the primary value can be delivered on one device, collaboration is limited, central reporting is not the core job, and users benefit from avoiding account setup or server-side custody.
It is a poor fit when the product requires real-time collaboration, cross-device continuity without manual backup, authoritative shared records, background processing, complex permissions, or server-controlled compliance workflows.
What comes next
InvoiceCraftly will continue to add document utilities, structured import and export, more flexible layouts, regional payment formats, and optional paid capabilities. The design challenge is to expand usefulness without quietly turning the no-signup core into a conventional account-based SaaS product.
That means future features will continue to pass through the same questions:
- Can this work locally?
- Does it require a network boundary?
- What exact data would leave the browser?
- Can the core editor still work if the service is unavailable?
- Can we explain the trade-off in one honest paragraph?
The result is not the simplest codebase in every dimension. It is a codebase aligned with the reason the product exists: professional invoicing without making people adopt invoicing software first.
See the architecture in use
Create a document without creating an account.
InvoiceCraftly creates invoices, quotes, estimates, receipts, and credit notes in the browser, with custom branding, optional reviewed payment QR codes, and downloadable exports.
Open the invoice editor