Skip to content
MocklyDocs

Build

Images, fonts, and files

Files in public/, resized images, and fonts for your pages.

Your site can host its own images, fonts, PDFs, and stylesheets. Put them in public/, and each one is served at its path without public: public/images/logo.png is /images/logo.png.

Mockly does some of the work for you. An <img> of an image from public/ gets resized copies and its size in pixels, so it loads fast and the page doesn't shift. A font you declare on <Page> is preloaded and gets a class of its own.

Files in public/#

KindFormats
ImagesPNG, JPEG, GIF, WebP, AVIF, SVG, ICO
FontsWOFF2, WOFF, TTF, OTF
DocumentsPDF
StylesheetsCSS, for a page's own styles

Nothing else can be hosted: no HTML, no scripts, and no video.

  • Paths use lowercase letters, digits, dots, dashes, and underscores, start each part with a letter or digit, and end in one of the extensions above. A path is at most 200 characters long and 8 folders deep.
  • Size. Each file can be up to 10 MB. In the studio, a site's files can add up to 100 MB.
  • Contents must match the extension. Each file is checked against its format's signature, so a renamed file is refused. An SVG must be UTF-8 text that contains <svg.

In the studio, add files with Upload files in the Files panel, or drop them on its public/ section. Names become URL-safe on the way in, so Team Photo.PNG becomes team-photo.png. With the CLI, put files in the site folder's public/ directory; a file that can't be hosted stops the build with a message naming it.

Using a file#

Refer to a file by its URL from the site's root:

TSX
<img src="/images/team.jpg" alt="The Acme team" /><a href="/guide.pdf">Download the guide</a>

A root-relative URL whose last segment has a dot names a file, since page routes never contain one. When no file in public/ matches, the compiler warns with MK2014. It checks URL attributes, icon and image on <Page>, and any string in your code that is a root-relative file URL, so a constant such as { photo: "/images/ada.png" } is checked too.

Images, video, and iframes can also come from other sites, by any http or https URL. data: URLs aren't allowed.

Publishing files#

The studio's preview shows the files in your draft right away. The live site gets them when you publish, and keeps the published version until the next publish, even if you replace or delete the file in the draft.

When you rename a file in the studio, the code that names it changes with it. Deleting a file lists the code that still names it.

Images#

When an <img> shows a PNG, JPEG, WebP, or AVIF from public/, Mockly fills in what the browser needs to load the right size:

TSX
<img src="/images/team.jpg" alt="The Acme team" />

For a photo 1500 pixels wide, the page gets something like this:

Text
<img src="/images/team.jpg" srcset="… 160w, … 320w, … 640w, … 960w, … 1280w, … 1500w"  sizes="(max-width: 1500px) 100vw, 1500px" width="1500" height="1000" alt="The Acme team">
  • srcset lists WebP copies at 160, 320, 640, 960, 1280, 1920, and 2560 pixels wide, those narrower than the image, plus one at the image's own width. A copy is kept only when it's smaller than the original file; otherwise the original takes its place, at its own width.
  • sizes is (max-width: Wpx) 100vw, Wpx, where W is the image's width: as wide as the screen, up to the image's own width. When CSS shows the image smaller than that, such as an avatar, give it your own sizes, like sizes="48px", and browsers download less.
  • width and height are the image's size in pixels, so the browser keeps room for it before it loads and nothing shifts. The stylesheet's reset still fits the image to its container.

An <img> is left as written when it has its own srcset, sits inside <picture>, or has a src that an island changes, and when it shows an SVG, an ICO, or a URL on another site. GIFs and animated images get width and height but no copies, since a copy would lose their frames.

When width and height are left out#

Mockly leaves out width and height when the <img> has either attribute already, and when its CSS might set only a height, which would stretch the image. That's a class that sets a height (h-…, min-h-…, or max-h-…) without a width (w-… or size-…), a class or style computed from an expression, or a style that mentions height. To get both, set the width too:

TSX
<img src="/images/logo.png" alt="Acme" class="h-8 w-auto" />

How copies are made#

In the studio, copies are made in the background after you upload an image. Publishing makes any that are still missing, so a published page always has them, and the preview uses the ones that exist so far. The CLI makes them on every build. Each copy is named by its content, so browsers can keep it for good.

Fonts#

Declare the fonts a page uses in <Page fonts>, with the font files in public/:

TSX
<Page title="Acme" fonts={[{ family: "Inter", src: "/fonts/inter.woff2", weight: "100 900" }]}>  <main class="font-inter">…</main></Page>

Each font gets:

  • A preload in the page's <head>, so the file starts downloading early.
  • An @font-face rule in the site's stylesheet.
  • A font-<name> class, made from the family's name in lowercase, with dashes for anything other than letters and digits. "Playfair Display" gets font-playfair-display. It works with variants, such as md:font-playfair-display.
FieldTypeDescription
familystringRequired. The family's name, in letters, digits, spaces, dashes, and underscores.
srcstringRequired. The font file, such as "/fonts/inter.woff2". It must end in .woff2, .woff, .ttf, or .otf.
weightnumber or stringA weight from 1 to 1000, such as 400, or a range for a variable font, such as "100 900".
stylestring"normal", "italic", or "oblique".
displaystringHow text shows while the font loads: "swap" (the default), "auto", "block", "fallback", or "optional".
fallbackstringWhat shows until the font loads, and if it can't: "sans-serif" (the default), "serif", or "monospace".
preloadbooleanWhether the page preloads the file. Defaults to true.

For a family without a variable font, add one entry per file, with the same family and each file's weight or style.

Values are checked when you publish. A bad one, such as a weight of 1200, stops the publish with MK1014, naming the field.

To use the same fonts on every page, keep the list in a constant in components/ and pass it to each page's <Page>, as in fonts={brand.fonts}. See Pages and routes for sharing <Page> props.

Stylesheets#

Most pages are styled with utility classes (see Components and styling). A page can also link CSS files of its own from public/:

TSX
<Page title="Acme" stylesheets={["/css/site.css"]}>

They load after the site's stylesheet, so they win where the two disagree. A URL inside a stylesheet is relative to the stylesheet, as usual. Published pages load stylesheets and fonts only from the site itself, so an @import of another site's stylesheet won't load.

To style a page with its own CSS alone, add utilities={false}: the page then loads neither Mockly's reset nor its utility classes, and its images are written as they are, without resized copies. Imported pages work this way.

Favicons and share images#

TSX
<Page  title="Acme"  icon="/favicon.svg"  image="/og.png"  imageAlt="Acme: launch pages that learn"  themeColor="#4f46e5">
  • icon is the favicon, such as an SVG, PNG, or ICO file. The page declares its type from the file's extension.
  • image is the picture social networks show when someone shares the page, ideally 1200×630. It's made absolute against the site's URL, since networks need a full URL, and the page gets a large Twitter card.
  • imageAlt describes image for people who can't see it.
  • themeColor tints the browser's interface on devices that support it.

icon and image written as strings are checked like any file URL, so one that matches no file in public/ is MK2014. Both can also be full https URLs.