als-render Documentation
Introduction
als-render
is a frontend framework that offers a comprehensive solution for building dynamic components (components with dynamic state and hooks), importing them, integrating them with each other, with the ability to use a single global context, and with the ability to use a JSX-like syntax.
The framework works on the fly both in the browser and on the server, offering SSR on the fly and allowing you to use the same files both on the server and in the browser.
On the server, you can connect the browser part via routes in Express and do it on the fly.
The framework also has the ability to localize for translation into pre-defined languages.
What's New in Version 3.0.0
The version 3.0.0 does not have backward compatibility. No
build
method andpublics
androutes
not available inside components any more.
- No publics and routes available inside components anymore
- The resultFn not running, on build without parameters any more
- New Lang version with add and merge methods
- No
build
method - props
- was
{data,lang}
- now
data
- was
- Available
context.lang
Installation and Setup
Installation
You can install als-render
via npm or use a CDN for quick testing in the browser:
npm (recommended for full development):
npm install als-render
CDN (for browser usage):
Requirements
- Browser: ES6 support is required.
- Node.js: Compatible with all Node.js versions supporting ES6.
Quick Start
Components example
App.js:
const Counter = require('./Counter');
class App extends Component {
constructor(props) { super(props)}
render(props) {
const { data } = props;
return (
<div>
<Counter count={data.count} />
</div>
);
}
}
module.exports = App;
Counter.js:
class Counter extends Component {
constructor(props) { super(props)}
render({ count }) {
return (
<div>
<button onclick={() => this.update({ count: count + 1 })}>
{ 'Increase' }
</button>
<span>{ count }</span>
<button onclick={() => this.update({ count: count - 1 })}>
{ 'Decrease' }
</button>
</div>
);
}
}
module.exports = Counter;
Browser Example
Here’s a minimal example of using als-render
in the browser to create a counter component:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://www.unpkg.com/als-render@3.0.0/render.min.js"></script>
<title>Counter</title>
</head>
<body>
<div component="App"></div>
</body>
<script>
render('./App', { count: 0 });
</script>
</html>
Node.js Example (Server-Side Rendering)
You can use the same components for server-side rendering (SSR). Here’s an example:
Server Code:
const Render = require('als-render');
const fs = require('fs');
const rendered = new Render('./App');
const rawHtml = rendered.render({ count: 0 });
const html = `<!DOCTYPE html>
<html lang="en">
<head><title>Counter</title></head>
<body>${rawHtml}</body></html>
`
fs.writeFileSync('./counter.html', rawHtml, 'utf-8');
Example with Express
For dynamic SSR integration with Express:
const express = require('express');
const Render = require('als-render');
const app = express();
const rendered = new Render('./App');
Render.publish(app, express); // Adds bundle and public routes.
app.get('/', (req, res) => {
const html = rendered.render({ count: 0 }, 'ru');
res.send(`<!DOCTYPE html>
<html lang="en">
<head><title>Counter</title></head>
<body>${html}</body>
</html>`);
});
app.listen(3000, () => console.log('Server is running on http://localhost:3000'));
In the browser: The server dynamically generates the same HTML structure as the browser example and serves it, including all component functionalities.
Components
als-render
uses als-component
to create reactive components that work seamlessly in both server and browser environments. Components support lifecycle hooks, event handling, and state management.
For detailed information on component usage, refer to the als-component documentation.
By default the Component class available as global variable for rendered components.
It can be used like this:
class App extends Component {
constructor(props) { super(props) }
render({ count = 0 }) {
return `
<div>
<button click="${this.action('click', () => this.update({ count: count - 1 }))}">-</button>
<span>${count}</span>
<button click="${this.action('click', () => this.update({ count: count + 1 }))}">+</button>
</div>
`;
}
}
Or like this if options.jsx = true
(true by default):
class App extends Component {
constructor(props) { super(props) }
render({ count = 0 }) {
return (
<div>
<button onclick={() => this.update({ count: count - 1 })}>-</button>
<span>{count}</span>
<button onclick={() => this.update({ count: count + 1 })}>+</button>
</div>
);
}
}
The Component
has hooks mount and unmount and onload event:
this.on('mount', () => console.log('Component mounted'));
this.on('unmount', () => console.log('Component unmounted'));
3.2 Localization
als-render
includes built-in localization support through a global _()
function, available in every component. This function retrieves the appropriate translation for the current language.
Example:
Define your localization settings:
const langs = {
langs: ['en', 'ru'],
dictionary: {
Increase: ['Increase', 'Увеличить'],
Decrease: ['Decrease', 'Уменьшить']
}
}
const options = { langs,lang: 'ru'};
In your component:
class Counter extends Component {
render({ count }) {
return (
<div>
<button>{ _('Increase') }</button>
<span>{ count }</span>
<button>{ _('Decrease') }</button>
</div>
);
}
}
In browser:
<script>
render('./App', {count:0}, { langs,lang: 'ru'});
</script>
On server:
const rendered = new Render('./App', {langs});
const lang = 'ru';
const rawHtml = rendered.render({ count: 0 }, lang);
Global variables
By default, there are few global variables which available for all components:
context
- is the object which passed from outside and available as global for all componentsisBrowser
- true in browser and false in NodeJs
_
- the localization function as showed above- Custom variables added by developer
context
const rendered = new Render('./App');
const context = {some:'Value for node'}
const browserContext = {some:'Value for browser'}
const lang = 'en'
const rawHtml = rendered.render({ count: 0 },lang,{context,browserContext});
class App extends Component {
constructor(props) { super(props) }
render({ count = 0 }) {
if(context.isBrowser) {
const existCount = localstorage.getItem('count')
if(existCount == undefined) localstorage.setItem('count',count)
else this.props.count = Number(existCount)
}
return (
<div>
{context.some}
<button onclick={() => this.update({ count: this.props.count - 1 })}>-</button>
<span>{this.props.count}</span>
<button onclick={() => this.update({ count: this.props.count + 1 })}>+</button>
</div>
);
}
}
Custom variables and scripts
New renderOptions
and bundleRenderOptions
in Constructor
Updated Constructor Interface
const renderInstance = new Render('./components/App', {
langs: { langs: ['ru'], dictionary: { hello: ['привет'] } },
includebundle: true,
renderOptions: {
parameters: [], // Additional parameters for server-side rendering
scriptBefore: '', // Scripts executed before server-side rendering
scriptAfter: '' // Scripts executed after server-side rendering
},
bundleRenderOptions: {
parameters: [], // Additional parameters for the browser bundle
scriptBefore: '', // Scripts executed before the browser bundle
scriptAfter: '' // Scripts executed after the browser bundle
}
});
Parameters
The parameters should be strings and can include default value, like ['some="test"',now=Date.now()]
.
Behavior of scriptBefore
and scriptAfter
scriptBefore
- script for execution before requiring files.- For example
const SOME_TOKEN="token";
. Now SOME_TOKEN available anywhere in rendered code.
- For example
scriptAfter
- script for execution after requiring files.- For example you have access to
modules
andresult
(MainComponent class) variables.
- For example you have access to
!imprtant Server bundle function builded twice to get publics and routes. On first run custom parameters are not available and scriptBefore and scriptAfter can throw error
Method render
render(data, lang, options = {}) {
const {
context = {}, // Context for server-side rendering
browserContext = {}, // Context for browser-side rendering
parameters = [], // Parameters for server-side rendering
bundleParameters = [] // Parameters for browser-side rendering (must be strings)
} = options;
}
parameters
- can be any js parameter from server. Even request and response variables.bundleParameters
- must be strings (e.g., throughJSON.stringify
) because they are passed to the browser bundle.
Example
const renderInstance = new Render('./components/App', {
langs: { langs: ['ru'], dictionary: { hello: ['привет'] } },
includebundle: true,
renderOptions: {
parameters: ['customParam'],
scriptAfter: 'if(customParam) customParam.test = true;'
},
bundleRenderOptions: {
parameters: ['customBundleParam'],
scriptAfter: 'customBundleParam.test = true;'
}
});
const customParam = { test: false };
const customBundleParam = { test: false };
const result = renderInstance.render(
{ key: 'value' },
'ru',
{
context: { user: 'test' },
browserContext: { browser: true },
parameters: [customParam],
bundleParameters: [JSON.stringify(customBundleParam)]
}
);
console.log(customParam.test); // true
console.log(result.includes('customBundleParam.test = true')); // true
SSR
There are few options for server side rendering.
- Don't include bundle for browser (set options.includebundle = false)
- Include bundle for browser (set options.includebundle = true, or leave it, cause it's true by default)
- Include bundle for browser as script inside page (default option)
- This option adds js code directly to html which encreasing the page size for download
- Include bundle as Express route
- This option, adds route to express routes (app.get...) and use bundle as script[src]
- Recomended for production, because bundle cached by browser
- You can add version to bundle in (options.version) for renewing the cache
- Include bundle for browser as script inside page (default option)
Example:
const express = require('express');
const app = express();
const Render = require('als-render');
Render.env.bundleAsRoute = true
Render.publics['/app/styles.css'] = './styles.css'
Render.routes['main-style.css'] = `body {margin:0}`
const rendered = new Render('./App',{ includebundle:true, version:'1.0.0' });
Render.publish(app, express); // Adds bundle and public routes.
Configuration Options
Render Class Interface
class Render {
static Require: typeof Require;
static publics: Record<string, string>; // Static public files or folders
static routes: Record<string, string>; // Static routes for Express
static env: {
bundleAsRoute: boolean; // Add bundle as route (default: false)
minify: boolean; // Minify the bundle (default: false)
jsx: boolean; // Enable JSX (default: true)
bundleName: string; // Name of the bundle function (default: 'bundleFn')
};
static publish(app: Express, express: typeof import('express')): void;
constructor(path: string, options?: {
includebundle?: boolean; // Include bundle in the result (default: true)
langs?: { langs: string[], dictionary: Record<string, string[]> };
defaultLang?: string; // Default language for `_()` (optional)
cyclicDependencies?: boolean; // Allow cyclic dependencies (default: false)
version:undefined; // Adds ?version={version} to bunlde as src
renderOptions: {
parameters?: string[], // Additional parameters for server-side rendering
scriptBefore?: string='', // Scripts executed before server-side rendering
scriptAfter?: string='' // Scripts executed after server-side rendering
},
bundleRenderOptions: {
parameters?: string[], // Additional parameters for the browser bundle
scriptBefore?: string='', // Scripts executed before the browser bundle
scriptAfter?: string=''// Scripts executed after the browser bundle
}
});
render(data: Record<string, any>, lang: string, options = {}) {
const {
context = {}, // Context for server-side rendering
browserContext = {}, // Context for browser-side rendering
parameters = [], // Parameters for server-side rendering
bundleParameters = [] // Parameters for browser-side rendering (must be strings)
} = options;
}
}
Browser render() Function Interface
async function render(
path: string,
data?: Record<string, any>,
options?: {
selector?: string; // CSS selector for root component
version?: string; // Cache-busting version string (optional)
jsx?: boolean; // Enable JSX syntax (default: true)
context?: Record<string, any>; // Global context object (default: {})
langs?: { langs: string[], dictionary: Record<string, string[]> }; // Localization settings
lang?: string; // Current language for `_()`
cyclicDependencies?: boolean; // Allow cyclic dependencies (default: false)
logger?: Console; // Logger for debugging (default: console)
parameters = {}; // must be {key:value}
scriptBefore = '';
scriptAfter = '';
}
): Promise<void>;