A modern JavaScript framework that renders components directly to the browser DOM with zero virtual DOM overhead. Build fast, SEO-optimized multi-page applications using familiar JSX syntax.
- Introduction
- Real-World Examples
- Installation & Project Structure
- Tutorial Section
- Creator & Support
Snapp is a modern JavaScript framework that renders components directly to the browser DOM without virtual DOM layers. It's designed for developers who want modern component architecture combined with the benefits of traditional multi-page applications.
- π Direct DOM Rendering - No virtual DOM means instant updates and superior performance
- π± SEO-First Design - Each page has its own HTML template for perfect search engine optimization
- π§Ή Smart Memory Management - Automatic event cleanup and memory leak prevention
- βοΈ JSX/TSX Support - Write components using familiar JSX syntax
- π¦ Multi-Page Architecture - Traditional routing with modern component development
- π§ Built-in Build System - esbuild integration for
.js,.jsx,.ts,.tsx
Snapp requires minimal learning - if you know JavaScript and HTML, you already know Snapp.
| Feature | Snapp | Other Frameworks |
|---|---|---|
| DOM Rendering | Direct to browser DOM | Virtual DOM diffing |
| SEO | Native HTML templates | SSR complexity |
| Performance | No reconciliation overhead | Virtual DOM overhead |
| Architecture | Multi-page apps | Single Page Apps |
| Learning Curve | Minimal - just JavaScript | Framework-specific concepts |
| Bundle Size | Tiny core library | Large framework + dependencies |
HTML Template (SEO-optimized):
<!-- user.html -->
<!DOCTYPE html>
<html>
<head>
<title>User Profile</title>
<meta name="description" content="View profile and latest activity">
</head>
<body id="snapp-app">
<div>
<h1>User Profile</h1>
<p>Username</p>
<p>Age</p>
<p>Email</p>
</div>
<script type="module" src="src/user.js"></script>
</body>
</html>This is what search engine crawlers see for SEO. If you don't plan on using server-side rendering, this approach provides the best SEO results. Server-side rendering is still possible with Snapp, but it's not covered in this guide.
Reusable Component:
// views/components/UserDetails.jsx
export default const UserDetails = (props) => {
return (
<>
<div>
<h2>Welcome, {props.data?.username || "Loading..."}</h2>
<p>Age: {props.data?.age || "Loading..."}</p>
<p>Email: {props.data?.email || "Loading..."}</p>
<p>Joined: {props.data?.joinDate || "Loading..."}</p>
</div>
</>
)
}Main Application Logic:
// views/user.jsx
import snapp from '../snapp.js';
import UserDetails from './components/UserDetails.jsx';
const App = () => {
const snappBody = document.querySelector("#snapp-app");
// Fetch user data and render when ready
fetch('/api/user/123')
.then(response => response.json())
.then(data => {
// Replace loading content with actual data
snapp.render(snappBody, <UserDetails data={data} />);
})
.catch(error => {
snapp.render(snappBody, (
<div>
<h2>Error Loading Profile</h2>
<p>Please try again later.</p>
</div>
));
});
// Return initial loading state
return <UserDetails data={null} />;
}
const snappBody = document.querySelector("#snapp-app");
snapp.render(snappBody, App());HTML Template:
<!-- login.html -->
<body>
<div id="snapp-app">
<h1>Account Access</h1>
<p>Click login to access your account</p>
</div>
<script type="module" src="src/login.js"></script>
</body>Interactive Component:
// views/login.jsx
import snapp from '../snapp.js';
const App = () => {
const showLoginForm = snapp.event("click", () => {
// Get form ID from backend
fetch('/api/auth/form')
.then(response => response.json())
.then(data => {
const formContainer = snapp.select("#formContainer");
snapp.render(formContainer, <LoginForm formId={data.id} />);
});
});
return (
<>
<div id="formContainer">
<p>Ready to login?</p>
</div>
<button event={[showLoginForm]}>Login</button>
</>
);
}
const LoginForm = (props) => {
const handleLogin = snapp.event("submit", (e) => {
e.preventDefault();
// Handle login logic
console.log("Login with form ID:", props.formId);
});
return (
<form event={[handleLogin]}>
<h3>Login (Form: {props.formId})</h3>
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
);
}
const snappBody = document.querySelector("#snapp-app");
snapp.render(snappBody, App());Note you install this once globally you do not need to install for all project
npm install -g snapp-kitRead more abould snapp-kit on github or on npm
# Create new Snapp project
snapp create my-awesome-app
cd my-awesome-app
# Start development with hot reload
snapp build
# Build for production deployment
snapp zipmy-snapp-app/
βββ views/ # π― Source JSX/TSX components
β βββ index.jsx # Main page component
β βββ about.jsx # About page component
β βββ user.jsx # User profile component
β βββ components/ # Reusable components
β βββ Header.jsx
β βββ Footer.jsx
β βββ UserCard.jsx
βββ src/ # π¦ Built JavaScript files (auto-generated)
β βββ index.js
β βββ about.js
β βββ user.js
βββ index.html # π Homepage template
βββ about.html # π About page template
βββ user.html # π User page template
βββ snapp.js # β‘ Snapp core library
- Write Components - Create
.jsx/.tsxfiles in theviews/folder - Auto-Build -
snapp buildwatches and compiles files to thesrc/folder - HTML Templates - Each page has its own HTML file for SEO optimization
- Link Together - HTML files import the built JavaScript from the
src/folder
# 1. Create HTML template
touch contact.html
# 2. Create JSX component
touch views/contact.jsx
# 3. The build system automatically detects and compiles new files
snapp build # Run this once again to enable hot reload during development
# 4. Snapp will build and generate all files in the src folderExample contact.html:
<!DOCTYPE html>
<html>
<head>
<title>Contact Us - Get In Touch</title>
<meta name="description" content="Contact our team for support and inquiries">
</head>
<body id="snapp-app">
<h1>Contact Us</h1>
<script type="module" src="src/contact.js"></script>
</body>
</html>Example views/contact.jsx:
// views/contact.jsx
import snapp from '../snapp'
import Button from 'components/Button.js'
const App = () => {
return (
<>
<h2>Contact Us</h2>
{/* Your contact page content */}
{/* Additional contact page elements */}
{/* More contact page features */}
</>
)
}
const snappBody = document.querySelector("#snapp-app");
snapp.render(snappBody, App());Snapp build will compile and auto-generate src/contact.js
NOTE: You only need to run 'snapp build' once - it will automatically recompile when you save your code!
<!-- index.html -->
<div id="snapp-body"></div>
<script type="module">
import snapp from './snapp.js'
import App from './src/index.js'
const snappBody = document.querySelector("#snapp-body");
snapp.render(snappBody, App());
</script>// views/index.jsx
export default const App = () => {
return <h2>Hello Snapp</h2>
}<!-- index.html -->
<div id="snapp-body"></div>
<script type="module" src="src/index.js"></script>// views/index.jsx
import snapp from '../snapp'
const App = () => {
return <h2>Hello Snapp</h2>
}
const snappBody = document.querySelector("#snapp-body");
snapp.render(snappBody, App());You can even use the body element:
<body id="snapp-body"></body>snapp.render(parentElement, component, type) // Render a component with specified type
snapp.render(parentElement, component, 'append') // Will append to existing content
snapp.render(parentElement, component) // Default behavior - replaces existing contentOther available types: 'prepend', 'after', 'before', 'replace'
Use snapp.on("DOM", callback) when you need to access DOM elements after they're rendered:
snapp.on("DOM", () => {
// This runs once the elements have been rendered to the DOM
// This is useful when you need to access the elements!
})Why do you need snapp.on("DOM")?
JavaScript runs before elements are rendered to the DOM, so you need snapp.on("DOM") to ensure elements exist before accessing them:
const App = () => {
snapp.select("#hello") // This runs first, before element is in DOM - returns null
snapp.on("DOM", () => {
// Runs once when elements are rendered to DOM
snapp.select("#hello") // This works because it runs after element is in DOM
})
const someFunction = () => {
snapp.select("#hello") // This works because the function runs when user interacts,
// which can only happen after element is in DOM
}
return <h2 onclick={() => someFunction()} id="hello">Hello Snapp</h2>
}snapp.select("#element") // Select single element
snapp.select(["#el1", "#el2"]) // Select multiple elements, returns array
snapp.selectAll(".class") // Select all elements with same class/selector
snapp.selectAll([".class1", ".class2"]) // Returns arrays of arrays of elementsconst loadNewMsg = snapp.event("click", callback)
const anotherEvent = snapp.event("click", callback)
<div id="loadMsg" event={[loadNewMsg]}>Click me</div>
// Multiple events on same element
<div event={[loadNewMsg, anotherEvent]}>Multiple events</div>You can also pass parameters to your events:
const loginBtn = snapp.event("click", (e, param) => {
// e is the element that was clicked (e.target)
// param contains your custom parameters
console.log(param) // {id: 36392375923}
})
<div event={[loginBtn, {id: 36392375923}]}>Click Snapp</div>Event Delegation: snapp.event uses event delegation - it adds one event listener to the document. This is optimal for applications with many interactive elements!
When an element is removed from the DOM, Snapp automatically removes that element's event instance and parameters. The snapp.event handler remains available for all other elements using the same event.
<button onclick={() => alert("Hello Snapp")}>Call Hello Snapp</button>
<button onClick={() => login()}>Login</button>
<button onclick={() => {
// Perform multiple actions
// Handle complex logic
// Execute additional functions
}}>Login</button>
// Snapp Attribute Naming
// Snapp follows HTML attribute naming conventions:
<button ondblclick={() => alert("Hi!")}>Double Click</button> // β
Will work
<button onDoubleClick={() => alert("Hi!")}>Double Click</button> // β Won't workSnapp uses HTML attribute naming and converts everything to lowercase. onClick is treated the same as onclick.
Note: Attribute names like "className" or "class" will work, "htmlFor" or "for" will work. All camelCase is converted to lowercase: "onClick" becomes "onclick", etc.
Snapp will support both HTML/JSX style attribute names in future versions, but currently uses HTML attribute naming conventions.
Snapp Cleanup: Snapp tracks each element and its event listeners! When an element is removed from the DOM, Snapp automatically removes all event listeners attached to that element!
const [msgBody, feedBody] = snapp.select(["#msgBody", "#feedBody"])
snapp.remove([msgBody]) // Remove msgBody from the DOM
const sayHello = snapp.event("click", () => {})
snapp.remove([sayHello]) // Remove the event listener
snapp.remove([msgBody, feedBody, sayHello]) // Remove elements from DOM and event listenersWhen you remove an event, all element parameters attached to it are automatically cleaned up!
const divStyle = snapp.css({
color: "red",
background: "red"
})
<div css={divStyle}>This is cool</div>You can use either syntax: "background": "red" or background: "red":
const divStyle = snapp.css({
"background-color": "red"
})
// Dynamic styling
const darkMode = false;
const style = snapp.css({
color: darkMode ? "white" : "black",
"background-color": darkMode ? "black" : "white"
})
<h2 css={style}>Hello Snapp</h2>
// Direct object without snapp.css
<h2 css={{color: "red"}}>Hi Snapp</h2>
// Using style attribute
<div style={{color: "red"}}>This is a div</div>Difference between snapp.css/css and style:
snapp.css/cssfollows CSS syntaxstylefollows JavaScript syntax
backgroundColor: "red" (camelCase) won't work with snapp.css and css={} but will work for style. "background-color": "red" (CSS syntax) works with both snapp.css and css but not with style!
const redBg = snapp.css({
"background-color": "red"
})
snapp.on("DOM", () => {
const myDiv = snapp.select("#myDiv")
snapp.applycss(myDiv, redBg)
snapp.applycss(myDiv, redBg, true) // Using 'true' replaces any existing inline styles
})
<div id="myDiv">My Div</div>Multiple elements:
const redBg = snapp.css({
"background-color": "red"
})
const anotherCss = snapp.css({
"color": "white"
})
snapp.on("DOM", () => {
const [myDiv, myHeader] = snapp.select(["#myDiv", ".myHeader"])
snapp.applycss([myDiv, myHeader], [redBg, anotherCss])
// applycss accepts arrays of elements and arrays of CSS
})snapp.applystyle is similar to applycss but supports JavaScript CSS syntax (camelCase):
snapp.on("DOM", () => {
const [hello, wow] = snapp.select(["#hello", "#wow"])
snapp.applystyle([hello, wow], {
fontSize: "50px"
})
})
<p id="hello">Hello World</p>
<p id="wow">Hello World</p>Cleanup: Use snapp.applycss(element, "", true) to remove all previous inline CSS/styles.
You should use snapp.applycss with snapp.css:
const myCSS = snapp.css({
"color": "blue"
})
snapp.applycss(element, myCSS)Kig Emmanuel - Framework Creator
- Passionate JavaScript developer who created Snapp to bridge the gap between modern component development and traditional multi-page applications
- Focused on performance, simplicity, and developer experience
- π Report Issues: GitHub Issues
- π‘ Feature Requests: GitHub Discussions
- π§ Direct Contact: Feel free to reach out with questions or suggestions
- β Show Support: Star the project on GitHub if Snapp helps you build better web applications!
We welcome contributions from the community! Whether it's:
- π Bug fixes
- β¨ New features
- π Documentation improvements
- π― Example applications
- π§ͺ Testing and feedback
Every contribution helps make Snapp better for everyone.
β‘ Built with Snapp Framework - Fast, Simple, Powerful
π Get Started β’ π Tutorial β’ π» GitHub
"Snapp: Where modern development meets traditional web architecture"
