Juris (JavaScript Unified Reactive Interface Solution)

repository·main·Indexed 42 days ago

https://github.com/jurisjs/juris

A browser-optimized, non-blocking reactive framework designed for high-performance, cross-platform applications. Juris features a zero-build, object-first architecture that supports progressive enhancement of existing HTML, a Virtual DOM convention, and a modular system including StateManager, HeadlessManager, and DOMEnhancer. It provides built-in support for reactive props, async operations, and integration with libraries like HTMX, Alpine.js, and Stimulus.

Tokens
190.3K
Snippets
438
Records
518
Agent score
49%

What's inside Juris

  1. What is Juris?

    main
    Juris (JavaScript Unified Reactive Interface Solution) is a non-blocking reactive framework designed to eliminate build complexity for applications of all sizes. It focuses on progressive enhancement, allowing developers to add reactivity to existing DOM structures without a heavy build step.
  2. Overview of @jurisjs/juris server features

    main

    The @jurisjs/juris server package is designed for high-performance, reactive, full-stack applications. Key capabilities include:

    Server-Side Rendering & Hydration

    • Server-Side Rendering (SSR): Fast initial page loads.
    • Smart Hydration: Efficient client-side takeover from SSR.
    • String Renderer: Specialized component for HTML generation during SSR.

    API & Routing

    • RESTful API Routes: Built-in endpoints with automatic routing.
    • Universal API Client: A fetch client that works seamlessly across both server and browser environments.
    • CORS Support: Ready for cross-origin requests.

    Performance & Runtime

    • High Performance: Capable of 1,300+ req/s with ~9.6ms response times.
    • Cross-Runtime Support: Compatible with Node.js (14+), Bun, and Deno.
    • Compression: Built-in Gzip compression for production environments.
  3. Review Juris limitations and ecosystem maturity

    main

    As a newer framework (released 2024), Juris has specific characteristics to consider during adoption:

    Current Limitations

    • New Ecosystem: Limited availability of third-party components and tools.
    • Small Community: Fewer tutorials and community resources compared to mature frameworks like React or Vue.
    • Tooling: Developer tools are currently more basic than established frameworks.
    • Learning Resources: Documentation and examples are still expanding.
    • Testing Ecosystem: While Juris has superior built-in testing capabilities, the external testing patterns and tools are still developing.
    • Design Systems: There are currently no established component libraries.

    Maturity Context

    Juris is currently in the 'New' stage of maturity with a 'Just Starting' community size, distinguishing it from mature ecosystems like React, Vue, or Angular.

  4. Compare Juris with mainstream frameworks

    main

    Juris is an All-in-One Platform built on an Object-first reactive architecture. It is designed for universal deployment, AI collaboration, and progressive enhancement.

    How it compares to other frameworks:

    • React: Unlike React's component-based virtual DOM approach, Juris uses an object-first reactive model.
    • Vue.js: While Vue is template-based, Juris focuses on a different reactive philosophy.
    • Angular: Juris differs from Angular's TypeScript-first enterprise patterns.
    • Svelte: Unlike Svelte's compile-time approach, Juris provides a runtime-capable platform.
    • Alpine.js: While Alpine is HTML-first for minimal JS, Juris is a more comprehensive platform.
  5. Optimize list rendering with the 'ignore' return value

    main

    When rendering lists, you can optimize DOM stability and performance using the ignore pattern.

    If the length of a list remains unchanged, returning "ignore" from your list rendering logic allows Juris to preserve the existing DOM structure. This enables individual items within the list to handle their own reactivity updates without triggering a full re-render of the list container.

  6. When to use Static vs Reactive children

    main

    Choosing the right type of children property is critical for performance:

    1. Static Children (Array): Use when the structure of the list or component tree does not change. This is ideal for fixed layouts or lists where items handle their own internal reactivity.

      • Example: children: [{ Header: {} }, { MainContent: {} }] or children: users.map(...).
    2. Reactive Children (Function): Use only when the actual structure of the DOM tree must change based on state (e.g., conditional rendering of entire components based on user roles).

      • Example: children: () => userRole === 'admin' ? [{ AdminPanel: {} }] : [{ LoginForm: {} }].
  7. How the CSS Extractor's three-layer system works

    main

    The CSS Extractor categorizes styles into three distinct layers to optimize performance:

    1. Static Styles: Extracted to native CSS classes for fast rendering (e.g., padding, display).
    2. Reactive Styles: Preserved as JavaScript functions that run when state changes (e.g., color: () => getState('theme.color')).
    3. Interactive States: Converted into event handlers (e.g., $hover, $active).

    When you provide a style object, the processor analyzes the properties, generates CSS for the static layer, modifies props to include the generated className and reactive styles, and sets up event handlers for interactive states.

    // Input Example:
    {
      style: {
        padding: '1rem',                      // Static
        color: () => getState('theme.color'), // Reactive
        $hover: { background: 'blue' }        // Interactive
      }
    }
    
    // Output Example:
    {
      className: 'j-component-abc123',        // CSS class for static styles
      style: {
        color: () => getState('theme.color')  // Preserved reactive styles
      },
      onmouseenter: (e) => { ... },          // Generated event handler
      onmouseleave: (e) => { ... }
    }
  8. Create Headless Components

    main

    Headless components allow you to define logic and APIs that can be consumed by other components without providing a UI. You define them in the headlessComponents property of the Juris constructor. They can expose an api object for method calls and use hooks (like onRegister) for setup logic. These are accessed via ctx.<componentName> within other components.

    const app = new Juris({
      headlessComponents: {
        geolocation: (props, ctx) => ({
          api: {
            getCurrentPosition() {
              navigator.geolocation.getCurrentPosition(
                pos => ctx.setState('geo.position', pos.coords)
              );
            }
          },
          hooks: {
            onRegister: () => console.log('Geolocation service ready')
          }
        })
      }
    });
    
    // Usage in a component
    const LocationDisplay = (props, ctx) => ({
      button: {
        text: 'Get Location',
        onclick: () => ctx.geolocation.getCurrentPosition()
      }
    });
  9. Achieving Temporal Independence with path-based reactivity

    main

    Juris achieves Temporal Independence by Default through an object-first architecture and path-based reactivity.

    Unlike traditional frameworks where a change to a single state variable can trigger a cascade of re-renders across an entire component tree, Juris allows you to define reactivity at the property level using functions that call context.getState(). This ensures that a component or sub-component only updates when the specific piece of state it is watching changes, eliminating unnecessary render waterfalls and the need for manual optimization patterns like React.memo.

    // Juris: Temporal independence through path-based reactivity
    const App = (props, context) => ({
      div: {
        children: [
          {
            UserProfile: {
              // Only updates when user data changes
              user: () => context.getState('user.profile')
            }
          },
          {
            PostsList: {
              // Only updates when posts change
              posts: () => context.getState('posts.items')
            }
          },
          {
            LoadingSpinner: {
              // Only updates when loading state changes
              loading: () => context.getState('ui.loading')
            }
          }
        ]
      }
    });
  10. Transform Object DOM into different formats using Transformation Engines

    main

    The Object DOM is platform-agnostic and can be converted into various target formats (HTML, React, Vue, Native, etc.) using transformation engines. This allows a single component definition to be deployed across web, mobile, and desktop platforms.

    Key transformation capabilities include:

    • HTML: Converts the object structure into standard HTML strings.
    • React: Converts the object into React.createElement calls, mapping properties like onclick to onClick and handling children via React.cloneElement.
    • Vue: Generates Vue component objects containing templates, data, methods, and computed properties.
    • Native (Mobile): Maps web elements (like div, span, button) to native components (like View, Text, TouchableOpacity for React Native; or Container, Text, ElevatedButton for Flutter/SwiftUI).
    // Example of a universal component definition
    const universalCard = {
      div: {
        className: 'card',
        style: {
          padding: '20px',
          borderRadius: '8px'
        },
        children: [
          {
            h3: {
              className: 'card-title',
              text: () => getTitle()
            }
          },
          {
            button: {
              className: 'card-action',
              text: 'Action',
              onclick: () => handleAction()
            }
          }
        ]
      }
    };
    
    // Deploying to different platforms
    const webOutput = juris.render(universalCard);
    const reactOutput = objectToReact(universalCard);
    const vueOutput = objectToVue(universalCard);
    const mobileOutput = objectToReactNative(universalCard);
  11. Implement Headless Component Architecture

    main

    Juris uses a headless component pattern to decouple business logic from UI implementation. This allows you to define pure logic components that manage state and provide an API, which can then be consumed by multiple different UI implementations (e.g., Mobile vs. Desktop).

    To create a headless component:

    1. Define a function that returns an object containing an api (methods to manipulate state) and optionally hooks (lifecycle methods).
    2. Use context.getState(path) to retrieve state and context.setState(path, value) to update it.
    3. Register the component using juris.registerHeadlessComponent(name, component, options).
    4. Access the logic in your UI components via context.ComponentName.
    // 1. Define pure logic
    const TodoLogic = (props, context) => ({
      api: {
        addTodo: (text) => {
          const todos = context.getState('todos', []);
          const newTodo = { id: Date.now(), text, completed: false };
          context.setState('todos', [...todos, newTodo]);
        },
        // ... other methods
      }
    });
    
    // 2. Register it
    juris.registerHeadlessComponent('TodoLogic', TodoLogic, { autoInit: true });
    
    // 3. Use in UI
    const MobileUI = (props, context) => ({
      div: {
        children: () => {
          const todos = context.getState('todos', []);
          const todoAPI = context.TodoLogic; // Access the registered API
          return [
            { MobileInput: { onSubmit: todoAPI.addTodo } },
            { MobileList: { todos } }
          ];
        }
      }
    });
  12. Use interactive states for full reactivity

    main

    While standard CSS pseudo-selectors (&:hover, &:active) are limited to static values or CSS variables, Juris Interactive States (prefixed with $) allow for full JavaScript reactivity. These are converted into event handlers, enabling complex logic and dynamic values during interactions.

    {
      // Standard CSS Pseudo-selectors (static only)
      '&:hover': {
        background: 'blue',
        color: 'var(--hover-color)'
      }
    
      // Juris Interactive States (full reactivity)
      $hover: {
        background: () => getState('theme.hoverBg'),
        color: () => computeContrastColor(),
        transform: () => `scale(${getState('ui.scale')})` 
      }
    }