
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
yet_another_d_render
Advanced tools
A lightweight hotwired-compatible lib with declarative programming in HTML
D-Render is a lightweight reactive UI library like Alpine.js, inspired by Vue and React. It provides a powerful component-based architecture with reactive state management and a comprehensive directive system.
The D-Render is built on three fundamental concepts:
Components - Self-contained, reusable UI elements that:
d-component or d-state attributesState Management - Reactive data model that:
d-state attribute or in component classesDirectives - HTML attributes that:
d- prefix (e.g., d-click, d-show)Components are initialized in two ways:
<!-- Using d-state -->
<div d-state="{ count: 0, displayed: true }">
<!-- Component content -->
</div>
<!-- Using d-component -->
<div d-component="MyComponent">
<!-- Component content -->
</div>
When D-Render initializes components, it attaches the component instance to DOM elements via a _dComponent property.
You can get the component instance using element._dComponent.
// Access the component instance from a DOM element
const element = document.querySelector('[d-component="MyComponent"]');
const component = element._dComponent;
// Use component methods and properties
component.setState({ count: 5 });
console.log(component.state);
Define custom components with extended functionality:
class MyComponent extends DRender.Component {
// Default state that merges with provided state
defaultState(state) {
return {
count: 0,
isVisible: true,
...state
}
}
// Component-specific directives
componentSpecificDirectives() {
return {
'd-custom': (component, node) => {
// Custom directive implementation
}
}
}
// Mixins for code reuse
mixins() {
return [MyMixin, AnotherMixin];
}
// Lifecycle hooks
afterInitialized() {
// Setup after component initialization
console.log('Component initialized');
}
stateChanged(prevState) {
// Handle state changes
console.log('State changed from', prevState, 'to', this.state);
}
childrenChanged(child) {
// Handle child component changes
console.log('Child component changed:', child);
}
unmounted() {
// Cleanup when component is destroyed
console.log('Component unmounted');
}
// State transitions for animations
transistionOnStateChanging(prevState, newState) {
// Return transition object for render() method
return { animate: true };
}
// Control child component rendering
shouldFollowRender(parent, transition) {
// Return false to prevent child re-rendering
return true;
}
// Custom methods
increment() {
this.setState({ count: this.state.count + 1 });
}
}
Reuse code across components using mixins:
const CounterMixin = (component) => ({
state: {
count: 0
},
increment() {
this.setState({ count: this.state.count + 1 });
},
decrement() {
this.setState({ count: this.state.count - 1 });
}
});
const LoggingMixin = (component) => ({
stateChanged(prevState) {
console.log('State changed:', prevState, '->', this.state);
}
});
class MyComponent extends DRender.Component {
mixins() {
return [CounterMixin, LoggingMixin];
}
}
// Simple state update
this.setState({ count: 5 });
// Functional state update
this.setState(prevState => ({ count: prevState.count + 1 }));
// With callback
this.setState({ count: 5 }, () => {
console.log('State updated');
});
// With transition and callback
this.setState({ count: 5 }, { animate: true }, () => {
console.log('State updated with transition');
});
<!-- Simple state update -->
<button d-click="{ count: count + 1 }">Click</button>
<!-- Method calls -->
<input d-change="handleChange">
<form d-submit="handleSubmit">
<!-- Multiple statements -->
<button d-click="increment(); logAction()">Increment</button>
Available events:
<!-- Prevent default and stop propagation -->
<button d-click.prevent.stop="handleClick">Click</button>
<!-- Debounced input (400ms default) -->
<input d-input.debounce="handleInput">
<!-- Custom debounce duration -->
<input d-input.debounce="handleInput" d-debounce-duration="1000">
<!-- Simple show/hide -->
<div d-show="isVisible">Shows/hides based on state</div>
<!-- Debounced show/hide -->
<div d-debounce-show="isVisible" d-debounce-duration="300">
Debounced visibility
</div>
<!-- Object syntax -->
<div d-class="{ active: isActive, 'text-danger': hasError }">
<!-- String syntax -->
<div d-class="dynamicClass">
<!-- Debounced class changes -->
<div d-debounce-class="{ active: isActive }" d-debounce-duration="200">
<div d-style="{ color: textColor, fontSize: size + 'px' }">
<div d-text="message"></div>
<div d-html="htmlContent"></div>
<!-- Text input -->
<input d-model="message">
<!-- Checkbox -->
<input type="checkbox" d-model="isChecked">
<!-- Radio buttons -->
<input type="radio" d-model="selectedOption" value="option1">
<input type="radio" d-model="selectedOption" value="option2">
<!-- Set DOM properties -->
<input d-prop="{ value: inputValue, placeholder: 'Enter text' }">
<!-- Set HTML attributes -->
<img d-attr="{ src: imageUrl, alt: imageAlt }">
<!-- Disabled/Readonly states -->
<button d-disabled="!isValid">Submit</button>
<input d-readonly="isViewOnly">
<!-- Basic loop -->
<div d-loop="items" d-loop-var="item">
<template>
<div d-key="item.id">
<p d-text="item.name"></p>
<p d-text="itemIndex"></p>
</div>
</template>
</div>
<!-- Object loop -->
<div d-loop="userObject" d-loop-var="user">
<template>
<div d-key="userKey">
<span d-text="userKey"></span>: <span d-text="user"></span>
</div>
</template>
</div>
<div d-component="ParentComponent">
<div d-component="ChildComponent">
<!-- ChildComponent can access ParentComponent via this.parent -->
</div>
</div>
In the child component:
class ChildComponent extends DRender.Component {
afterInitialized() {
// Access parent component
const parentState = this.parent.state;
// Call methods on parent component
this.parent.someMethod();
// Update parent state
this.parent.setState({ parentUpdated: true });
// Access root component
this.root.setState({ rootUpdated: true });
}
}
class ParentComponent extends DRender.Component {
afterInitialized() {
// Get all child components
const allChildren = this.children;
// Filter children by component name
const specificChildren = this.filterChildren('ChildComponent');
// Access child state
const childState = specificChildren[0].state;
}
}
Shadow components proxy to their parent for shared state:
<div d-component="ParentComponent" d-state="{ sharedCount: 0 }">
<div d-component="ShadowComponent">
<!-- This component shares state with parent -->
</div>
</div>
Portal elements allow you to render component content in different DOM locations:
<!-- Define portal targets -->
<div id="modal-container"></div>
<div id="notification-container"></div>
<!-- Component with portal support -->
<div d-component="ModalComponent" d-global-directives>
<div class="modal-content">
<!-- This will appear in original location -->
<h2>Modal Title</h2>
<p>Modal Content</p>
</div>
</div>
<!-- Portal targets will receive component content -->
<div d-portal="ModalComponent"></div>
Components can make their directives available globally:
<!-- Component with global directives -->
<div d-component="ModalManager" d-global-directives d-state="{ isOpen: false }">
<div class="modal" d-class="{ active: isOpen }">
<div class="modal-content">
<h2>Modal</h2>
<button d-click="{ isOpen: false }">Close</button>
</div>
</div>
</div>
<!-- Use component directives globally -->
<button d-modal-manager-click="{ isOpen: true }">
Open Modal
</button>
<!-- Global refs -->
<button d-modal-manager-ref="openButton">Open</button>
<div d-on-state-change="handleStateChange(prevState)">
<div d-on-render="handleRender(transition)">
<div d-after-initialized="handleInit">
Access DOM elements through refs:
<!-- Single ref -->
<input d-ref="usernameInput">
<!-- Array ref -->
<div d-ref="items[]">Item 1</div>
<div d-ref="items[]">Item 2</div>
<!-- Component-specific refs -->
<button d-my-component-ref="submitButton">Submit</button>
In component:
class MyComponent extends DRender.Component {
afterInitialized() {
// Access refs
const input = this.refs.usernameInput;
const items = this.refs.items; // Array
const submitBtn = this.refs.submitButton;
input.focus();
}
}
D-Render automatically detects DOM changes and updates components:
// Components are automatically created/destroyed when DOM changes
const newElement = document.createElement('div');
newElement.setAttribute('d-component', 'MyComponent');
document.body.appendChild(newElement); // Component automatically initialized
Enable debug logging for development:
// Enable debug features
DRender.debug.logAllFuncStr = true; // Log function compilation
DRender.debug.logCompiledFuncExecutionError = true; // Log execution errors
DRender.debug.logAttributeChanges = true; // Log attribute changes
DRender.debug.keepDirectives = true; // Keep directives in DOM for inspection
// Find components by name
const components = DRender.findComponents('MyComponent');
// Get component hierarchy
DRender.graphComponents(); // Console log component tree
// Access closest component to element
const component = element.closestComponent();
// Access component depth
const depth = component.depth;
// Get component name formats
const kebabName = component.kebabName; // my-component
const kebabPrefix = component.kebabPrefix; // d-my-component
// Debounced rendering
this.setState({ count: 5 }, {}, false); // Don't trigger render
this.setState({ count: 6 }, {}, true, true); // Immediate render
// Control child rendering
shouldFollowRender(parent, transition) {
// Prevent unnecessary child re-renders
return this.state.shouldUpdateChildren;
}
element - DOM element associated with componentstate - Component state objectparent - Parent component instancechildren - Array of child componentsroot - Root component instancerefs - Object containing DOM element referencesdepth - Component depth in hierarchyname - Component namekebabName - Kebab-case component namekebabPrefix - Directive prefix for global directivessetState(state, transition, triggerRendering, immediateRendering) - Update component statedestroy() - Clean up component and remove event listenersfilterChildren(name) - Get child components by namefindRefs() - Get all DOM element referencesclosestComponent(element) - Get closest component to elementdefaultState(state) - Define default state valuesafterInitialized() - Called after component initializationstateChanged(prevState) - Called when state changeschildrenChanged(child) - Called when child components changeunmounted() - Called when component is destroyedtransistionOnStateChanging(prevState, newState) - Define state transitionsshouldFollowRender(parent, transition) - Control child renderingmixins() - Return array of mixins to applycomponentSpecificDirectives() - Define custom directivesrun() - Initialize D-Render and scan DOM for componentsregisterComponents(...components) - Register component classesdefineComponent(name, ...mixins) - Define component with mixinsfindComponents(name, scopeNode) - Find components by namegraphComponents(html, scopeNode) - Log component hierarchyclosestComponent(node) - Get closest component to elementaddHelpers() - Add global helper methods to windowcreateComponent(node, options) - Create component from elementComponent - Base component classDirectives - Object containing all directive implementationsPrefixes - Object containing event modifier implementationsdebug - Debug configuration objectd-click - Click event handlerd-change - Change event handlerd-input - Input event handlerd-keyup - Keyup event handlerd-keypress - Keypress event handlerd-submit - Submit event handlerd-focus - Focus event handlerd-blur - Blur event handler.prevent - Call event.preventDefault().stop - Call event.stopPropagation().debounce - Debounce event handlerd-show - Show/hide element based on conditiond-debounce-show - Debounced show/hided-disabled - Enable/disable elementd-readonly - Make element read-onlyd-model - Two-way data bindingd-text - Set text contentd-html - Set HTML contentd-value - Set element valued-class - Bind CSS classesd-style - Bind inline stylesd-prop - Set DOM propertiesd-attr - Set HTML attributesd-loop - Loop over arrays/objectsd-key - Unique key for loop itemsd-on-state-change - State change hookd-on-render - Render hookd-after-initialized - Initialize hookd-ref - DOM element referenced-component-ref - Component-specific reference<div d-component="TodoApp" d-state="{
todos: [
{ id: 1, text: 'Learn D-Render', completed: true },
{ id: 2, text: 'Build an app', completed: false }
],
newTodo: '',
filter: 'all'
}">
<h1>Todo List</h1>
<!-- Add new todo -->
<div class="input-group">
<input
d-model="newTodo"
d-keypress.enter="addTodo"
placeholder="What needs to be done?"
class="todo-input"
d-ref="newTodoInput">
<button d-click="addTodo">Add</button>
</div>
<!-- Todo filters -->
<div class="filters">
<button
d-click="{ filter: 'all' }"
d-class="{ active: filter === 'all' }">All</button>
<button
d-click="{ filter: 'active' }"
d-class="{ active: filter === 'active' }">Active</button>
<button
d-click="{ filter: 'completed' }"
d-class="{ active: filter === 'completed' }">Completed</button>
</div>
<!-- Todo list -->
<ul class="todo-list">
<div d-loop="filteredTodos()" d-loop-var="todo">
<template>
<li d-key="todo.id">
<div d-class="{ completed: todo.completed }">
<input
type="checkbox"
d-prop="{ checked: todo.completed }"
d-change="toggleTodo(todo.id)">
<span d-text="todo.text"></span>
<button
class="delete-btn"
d-click="removeTodo(todo.id)">×</button>
</div>
</li>
</template>
</div>
</ul>
<!-- Status and bulk actions -->
<div class="todo-footer" d-show="todos.length > 0">
<span d-text="remainingCount() + ' items left'"></span>
<button
d-click="clearCompleted"
d-show="todos.some(t => t.completed)">
Clear completed
</button>
</div>
<!-- Empty state -->
<p d-show="todos.length === 0" class="empty-state">
No todos yet! Add one above.
</p>
</div>
<script>
class TodoApp extends DRender.Component {
filteredTodos() {
if (this.state.filter === 'active') {
return this.state.todos.filter(todo => !todo.completed);
} else if (this.state.filter === 'completed') {
return this.state.todos.filter(todo => todo.completed);
} else {
return this.state.todos;
}
}
remainingCount() {
return this.state.todos.filter(todo => !todo.completed).length;
}
addTodo() {
const text = this.state.newTodo.trim();
if (text) {
this.setState({
todos: [...this.state.todos, {
id: Date.now(),
text,
completed: false
}],
newTodo: ''
}, () => {
// Focus input after adding
this.refs.newTodoInput.focus();
});
}
}
toggleTodo(id) {
this.setState({
todos: this.state.todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
});
}
removeTodo(id) {
this.setState({
todos: this.state.todos.filter(todo => todo.id !== id)
});
}
clearCompleted() {
this.setState({
todos: this.state.todos.filter(todo => !todo.completed)
});
}
}
DRender.registerComponents(TodoApp);
DRender.run();
</script>
<!-- Modal manager component -->
<div d-component="ModalManager" d-global-directives d-state="{
isOpen: false,
title: '',
content: ''
}">
<div class="modal-backdrop" d-class="{ active: isOpen }" d-click="{ isOpen: false }">
<div class="modal" d-click.stop>
<div class="modal-header">
<h3 d-text="title"></h3>
<button d-click="{ isOpen: false }}">×</button>
</div>
<div class="modal-body" d-html="content"></div>
<div class="modal-footer">
<button d-click="{ isOpen: false }">Close</button>
</div>
</div>
</div>
</div>
<!-- Using modal globally -->
<button d-modal-manager-click="{
isOpen: true,
title: 'Hello World',
content: '<p>This is a modal!</p>'
}">
Open Modal
</button>
<!-- Modal with component-specific ref -->
<button d-modal-manager-ref="openModalBtn">Open Modal</button>
State Management
setState() to update stateComponent Design
unmounted()Performance
shouldFollowRender() to optimize child renderingDirectives
DRender.debug.logAllFuncStr = true;
DRender.debug.logCompiledFuncExecutionError = true;
DRender.debug.logAttributeChanges = true;
DRender.debug.keepDirectives = true;
// View component tree
DRender.graphComponents();
// Find components
const components = DRender.findComponents('MyComponent');
// Access component from element
const component = element._dComponent;
// Use global helpers
graphComponents();
findComponents();
$0.closestComponent;
// Log state changes
class MyComponent extends DRender.Component {
stateChanged(prevState) {
console.log('State changed:', prevState, '->', this.state);
}
}
setState() to update state - never modify state directlyshouldFollowRender() for performance optimizationFAQs
A lightweight hotwired-compatible lib with declarative programming in HTML
We found that yet_another_d_render demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.