1.1.9 • Published 4 months ago

feather-render v1.1.9

Weekly downloads
-
License
ISC
Repository
-
Last release
4 months ago

Feather Render

gzip license version

✨ A feather light render framework ✨ 621 bytes minified and gzipped - no dependencies - SSR support

Companion frameworks:

Live examples:

coffee

Getting started

Package

npm i feather-render

...or inline

<head>
  <script src="feather-render.min.js"></script>
</head>
<body>
  <script>
    const { html, hydrate } = window.__feather__ || {};
  </script>
</body>

Index

Usage

Documentation

Examples

Usage

Basic syntax

import { html } from 'feather-render';

const TodoItem = ({ todo }) => {
  return html`
    <li>${todo.title}</li>
  `;
};

const TodoList = () => {
  return html`
    <ul>${todos.map(todo => TodoItem({ todo })).join('')}</ul>
  `;
};

const Document = () => html`
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Feather</title>
      <script type="module" src="index.js"></script>
    </head>
    <body>
      ${TodoList()}
    </body>
  </html>
`;

Tip: Plugins for VSCode like lit-html or Inline HTML can be used for syntax highlighting.

Server-Side Rendering (SSR)

import express from 'express';
import { Document } from './components/Document';

const server = express();

server
    .get('/', (req, res) => {
        res.send(Document().toString());
    })
    .use(express.static('dist'));

server.listen(5000);

Client hydration

import { hydrate } from 'feather-render';
import { TodoList } from './components/TodoList.js';

hydrate(TodoList(), document.body);

Documentation

html()

const { refs, render, mount, unmount } = html`<div></div>`;

Parameters

  • string - html template string to render

Return value: Render

  • refs - list of id'ed elements
  • render - return of functional component
  • mount() - run callback on mount
  • unmount() - run callback on unmount

html().mount()

mount(() => {
  console.log('Component inserted in DOM');
});

Parameters

  • callback() - function called when component is inserted in DOM

Return value

  • void

html().unmount()

unmount(() => {
  console.log('Component removed from DOM');
});

Parameters

  • callback() - function called after component is removed from DOM

Return value

  • void

hydrate()

hydrate(App(), document.body);

Parameters

  • element - Render from html()
  • target - where to mount the DOM

Return value

  • void

Examples

Re-rendering

Primitive values

import { store } from 'feather-state';
import { html } from 'feather-render';

const { watch, ...state } = store({
  greeting: 'Hello, World'
});

const Component = () => {
  const { refs, render } = html`
    <p id="paragraph">${state.greeting}</p>
  `;

  // Watch greeting + update DOM
  watch(state, 'greeting', (next) => {
    refs.paragraph?.replaceChildren(next);
  });

  // Change greeting state
  setTimeout(() => {
    state.greeting = 'Hello, back!';
  }, 1000);

  return render;
};

Lists

import { store } from 'feather-state';
import { html } from 'feather-render';

const { watch, ...state  } = store({
  todos: ['Todo 1', 'Todo 2'];
});

const TodoItem = ({ todo }) => {
  return html`
    <li>${todo}</ul>
  `;
};

const TodoList = () => {
  const { refs, render, mount } = html`
    <ul id="todoList">
      ${state.todos.map(todo => (
        TodoItem({ todo })
      )).join('')}
    </ul>
  `;

  const reRenderTodos = () => {
    const fragment = new DocumentFragment();
    for (let todo of todoStore.todos) {
      const { element } = TodoItem({ todo });
      element && fragment.appendChild(element);
    }
    refs.todoList?.replaceChildren(fragment);
  };

  // Watch todos + update DOM
  watch(state, 'todos', () => {
    reRenderTodos();
  });

  // Hydrate TodoItems
  mount(() => {
    reRenderTodos();
  });

  // Append todo in state
  setTimeout(() => {
    state.todos = [...state.todos, 'Todo 3'];
  }, 1000);

  return render;
};

Event listeners

Form submission

import { html } from 'feather-render';

const Component = () => {
  const { refs, render, mount, unmount } = html`
    <form id="form">
      <p id="status">Fill in form</p>
      <input type="text" />
      <button type="submit">Submit</button>
    </form>
  `;

  const handleSubmit = (event) => {
    event.preventDefault();
    refs.status?.replaceChildren('Submitting');
  };

  mount(() => {
    refs.form?.addEventListener('submit', handleSubmit);
  });
  unmount(() => {
    refs.form?.removeEventListener('submit', handleSubmit);
  });

  return render;
};

Fetching

Server and client

const App = () => {
  const { render } = html``;

  fetch('http://localhost:5000/api/v1/user')
    .then(res => res.json())
    .then(res => console.log(res));

  return render;
};

Server or client

const isServer = () => typeof window === 'undefined';
const isClient = () => typeof window !== 'undefined';

const App = () => {  
  const { render } = html``;

  if (isServer()) {
    fetch('http://localhost:5000/api/v1/user')
      .then(res => res.json())
      .then(res => console.log(res));
  }

  if (isClient()) {
    fetch('http://localhost:5000/api/v1/user')
      .then(res => res.json())
      .then(res => console.log(res));
  }

  return render;
};

On mount

const App = () => {
  const { render, mount } = html``;

  mount(() => {
    fetch('http://localhost:5000/api/v1/user')
      .then(res => res.json())
      .then(res => console.log(res));
  });

  return render;
};

Other

Lazy / Suspense

const App = () => {
  const { render } = html`
    <div id="lazyParent"></div>
  `;

  import('./LazyComponent').then(({ LazyComponent }) => {
    const { element } = LazyComponent();
    element && refs.lazyParent?.replaceChildren(element);
  });

  return render;
};

Unique id's

let i = 0;
export function id(name: string) {
  return `${name}_${i++}`;
}
import { id } from '../helpers/id';

const App = () => {
  const uniqueId = id('unique');

  const { refs, render, mount } = html`
    <div id=${uniqueId}></div>
  `;

  mount(() => {
    refs[uniqueId]?.replaceChildren('Component mounted');
  });

  return render;
};

Roadmap 🚀

  • CLI tool
  • Automatically hydrate child components
  • Cleaner way of referencing values in html
  • Binding values, re-renders and listeners
  • CSS in JS examples
1.1.9

4 months ago

1.1.8

4 months ago

1.1.7

4 months ago

1.1.6

4 months ago

1.1.5

4 months ago

1.1.4

4 months ago

1.1.3

4 months ago

1.1.1

4 months ago

1.1.0

4 months ago

1.1.2

4 months ago

1.0.2

4 months ago

1.0.1

5 months ago

1.0.0

5 months ago

0.1.0

5 months ago