I have asked Claude to create a doc for new Juris users.
Juris Framework - Complete Beginner's Guide
What is Juris?
Juris is a simple JavaScript framework that helps you build interactive web applications. Think of it like a toolkit that makes it easy to:
- Create reusable pieces of your webpage (called "components")
- Handle user interactions (like clicking buttons or typing in forms)
- Keep track of data that changes (like user input or loading states)
Unlike complex frameworks, Juris uses plain JavaScript objects to describe what you want your webpage to look like.
Your First Juris App
Let's start with the simplest possible example:
// Step 1: Create a simple component
const HelloWorld = () => {
return {
h1: {
text: "Hello, World!",
style: {
color: "blue",
"text-align": "center"
}
}
};
};
// Step 2: Create the app
const app = new Juris({
components: {
HelloWorld
},
layout: [
{ HelloWorld: {} }
]
});
// Step 3: Show it on the page
app.render();
What just happened?
- We created a component called
HelloWorld
that returns a description of anh1
element - We told Juris about our component and where to put it
- We rendered it to the page
Understanding Components
Components are like LEGO blocks for your webpage. Each component describes a piece of your user interface.
Basic Component Structure
const MyComponent = () => {
return {
elementName: {
text: "What to display",
style: { /* How it should look */ },
// More properties...
}
};
};
Different Types of Elements
const ExampleElements = () => {
return {
div: {
children: [
{
h1: { text: "This is a heading" }
},
{
p: { text: "This is a paragraph" }
},
{
button: {
text: "Click me!",
style: { backgroundColor: "green", color: "white" }
}
},
{
input: {
placeholder: "Type something here...",
style: { padding: "10px", fontSize: "16px" }
}
}
]
}
};
};
Making Things Interactive with State
State is like the "memory" of your app. It remembers things like what the user typed, whether a button was clicked, etc.
Your First Interactive App
const Counter = (props, { getState, setState }) => {
return {
div: {
style: { textAlign: "center", padding: "20px" },
children: [
{
h2: {
text: () => `Count: ${getState("count") || 0}`
}
},
{
button: {
text: "Add 1",
onclick: () => {
const currentCount = getState("count") || 0;
setState("count", currentCount + 1);
},
style: {
padding: "10px 20px",
fontSize: "16px",
margin: "10px"
}
}
}
]
}
};
};
const app = new Juris({
components: { Counter },
states: {
count: 0 // Starting value
},
layout: [
{ Counter: {} }
]
});
app.render();
Key concepts:
-
getState("count")
- Gets the current value -
setState("count", newValue)
- Updates the value -
text: () => ...
- Use a function for text that changes -
onclick: () => ...
- Handle button clicks
Building a Real App: Todo List
Let's build something useful - a todo list app:
// Component to add new todos
const TodoInput = (props, { getState, setState }) => {
return {
div: {
style: { marginBottom: "20px" },
children: [
{
input: {
placeholder: "What needs to be done?",
style: {
padding: "10px",
fontSize: "16px",
width: "300px"
},
onkeypress: (e) => {
if (e.key === 'Enter' && e.target.value.trim()) {
const todos = getState("todos") || [];
const newTodo = {
id: Date.now(),
text: e.target.value.trim(),
completed: false
};
setState("todos", [...todos, newTodo]);
e.target.value = ''; // Clear input
}
}
}
}
]
}
};
};
// Component to show the list of todos
const TodoList = (props, { getState, setState }) => {
return {
div: {
children: () => {
const todos = getState("todos") || [];
return todos.map(todo => ({
div: {
style: {
padding: "10px",
border: "1px solid #ddd",
margin: "5px 0",
backgroundColor: todo.completed ? "#f0f0f0" : "white"
},
children: [
{
input: {
type: "checkbox",
checked: todo.completed,
onchange: (e) => {
const todos = getState("todos");
const updatedTodos = todos.map(t =>
t.id === todo.id
? { ...t, completed: e.target.checked }
: t
);
setState("todos", updatedTodos);
}
}
},
{
span: {
text: todo.text,
style: {
marginLeft: "10px",
textDecoration: todo.completed ? "line-through" : "none"
}
}
}
]
}
}));
}
}
};
};
// Main app component
const TodoApp = () => {
return {
div: {
style: {
maxWidth: "600px",
margin: "0 auto",
padding: "20px"
},
children: [
{
h1: {
text: "My Todo List",
style: { textAlign: "center" }
}
},
{ TodoInput: {} },
{ TodoList: {} }
]
}
};
};
// Create and run the app
const app = new Juris({
components: {
TodoApp,
TodoInput,
TodoList
},
states: {
todos: []
},
layout: [
{ TodoApp: {} }
]
});
app.render();
Adding External Services
Sometimes you need to do things like make API calls or log information. Juris lets you define "services" for this:
// Define services
const app = new Juris({
services: {
api: {
saveData: (data) => {
console.log("Saving:", data);
// Here you could send to a server
localStorage.setItem('myAppData', JSON.stringify(data));
},
loadData: () => {
const saved = localStorage.getItem('myAppData');
return saved ? JSON.parse(saved) : null;
}
}
},
components: {
SaveButton: (props, { getState, setState, api }) => {
return {
button: {
text: "Save Data",
onclick: () => {
const currentData = getState("userData");
api.saveData(currentData);
alert("Data saved!");
}
}
};
}
},
// ... rest of app config
});
Listening to Changes
You can "subscribe" to changes in your app's state to do things like save data automatically:
// Save todos whenever they change
app.subscribe("todos", (oldTodos, newTodos) => {
console.log(`Todo count changed from ${oldTodos?.length || 0} to ${newTodos.length}`);
// Auto-save to browser storage
localStorage.setItem('todos', JSON.stringify(newTodos));
});
Loading Data When App Starts
const app = new Juris({
// ... your components and config
states: {
todos: JSON.parse(localStorage.getItem('todos') || '[]')
}
});
Common Patterns and Tips
1. Handling Forms
const ContactForm = (props, { getState, setState }) => {
return {
form: {
children: [
{
input: {
placeholder: "Your name",
oninput: (e) => setState("name", e.target.value)
}
},
{
input: {
placeholder: "Your email",
type: "email",
oninput: (e) => setState("email", e.target.value)
}
},
{
button: {
text: "Submit",
onclick: (e) => {
e.preventDefault();
const name = getState("name");
const email = getState("email");
alert(`Hello ${name}! We'll contact you at ${email}`);
}
}
}
]
}
};
};
2. Conditional Rendering
const LoginStatus = (props, { getState }) => {
return {
div: {
text: () => {
const user = getState("currentUser");
return user ? `Welcome, ${user.name}!` : "Please log in";
}
}
};
};
3. Lists and Loops
const ShoppingList = (props, { getState }) => {
return {
ul: {
children: () => {
const items = getState("shoppingItems") || [];
return items.map(item => ({
li: { text: item.name }
}));
}
}
};
};
Next Steps
Now that you understand the basics:
- Practice: Try building small apps like a calculator, weather display, or photo gallery
- Experiment: Try different HTML elements, CSS styles, and event handlers
- Build bigger: Combine multiple components to create more complex applications
- Learn async: Explore loading data from APIs using async components
Quick Reference
Component Structure
const MyComponent = (props, { getState, setState, ...services }) => {
return {
elementName: {
text: "content" || () => "dynamic content",
style: { cssProperty: "value" },
children: [/* array of child elements */],
onclick: (event) => { /* handle clicks */ },
oninput: (event) => { /* handle input */ }
}
};
};
App Setup
const app = new Juris({
services: { /* your services */ },
components: { /* your components */ },
states: { /* initial state */ },
layout: [{ ComponentName: { prop: "value" } }]
});
app.render();
app.subscribe("stateKey", (old, new) => { /* react to changes */ });
That's it! You now know enough Juris to build real applications. Start small, experiment, and have fun building!
Top comments (0)