Skip to main content
Deno 2 is finally here 🎉️
Learn more

fresh_layout

Layouts for Deno Fresh.

Installation

Add fresh_layouts dependency to your imports_map.json file:

{
  "imports": {
    ...,
    "$fresh_layout/": "https://deno.land/x/fresh_layout@0.1.0/"
  }
}

Usage

There are two ways to apply layouts to route pages:

In main.ts file, import applyManifestLayouts from "$fresh_layouts/mod.ts" and call applyManifestLayouts with manifest. Use the returned Manifest object to call the start function.

...
import { applyManifestLayouts } from "$fresh_layouts/mod.ts";
...
const newManifest = applyManifestLayouts(manifest);
await start(newManifest, { plugins: [twindPlugin(twindConfig)] });

This way enables the use of _layout.(js|jsx|ts|tsx) files within the routes/ directory.

How layouts are applied?

Layouts are applied to every page route in its directory, and to every layout or page route down in the directory tree. Here is a simple example:

routes/             |
|  slug/            |
|  |  [id]/         |
|  |  |  index.tsx  |  1
|  |  index.tsx     |  2
|  |  _layout.tsx   |  3 wraps (2) and (1)
|  [name].tsx       |  4
|  _layout.tsx      |  5 wraps (4), (3) wrapping (2) and (3) wrapping (1)

NOTE: (named) layouts aren’t supported yet. I’m figuring out how to support them. Any help is appreciated :)

Per-route way

Write layout components anywhere, import them in your page route file and call applyLayouts with your page component and the layouts in the order they will be applied:

// routes/path/to/page.tsx
import { applyLayouts } from "$fresh_layouts/mod.ts";

import { mainLayout } from "path-to/main-layout.tsx";
import { secondLayout } from "path-to/second-layout.tsx";
import { thirdLayout } from "path-to/third-layout.tsx";

export default applylayouts(() => {
  return <p>Do you like arrow functions?</p>;
}, [mainLayout, secondLayout, thirdLayout]);

The above example will apply intuitively the layouts like this:

<MainLayout>
  <SecondLayout>
    <ThirdLayout>
      <p>Do you like arrow functions?</p>
    </ThirdLayout>
  </SecondLayout>
</MainLayout>

Both?

Yes, both ways can be used together. Just keep in mind that per-route layouts apply first.

The Layout component

export const layout: Layout = (child: Page, props?: PageProps) => {
  return <>{/* JSX Code */}</>;
};

In order to render the child component, call:

child(props); // remember to use {} when calling inside JSX

Here are the complete type definitions of Layout and Page type aliases:

export type Page<Data = any> = (props?: PageProps<Data>) => JSX.Element;

export type Layout<Data = any> = (
  child: Page<Data>,
  props?: PageProps<Data>
) => JSX.Element;

It might feel strange that, even when layouts can wrap others, a Layout component always receive a Page component as child. That’s because every time a layout wraps a page, it is wrapped by a Page component.

// src/mod.ts
function wrap<Data = any>(page: Page<Data>, layout: Layout<Data>) {
  return (props?: PageProps<Data>) => layout(page, props);
}

Contributing

You can open an issue or make a PR, I’ll try to check and merge (if possible) quickly.

License

Under MIT license.