The Priority System

Learn how JopiJS uses priorities to handle code overrides, component replacements, and module extensions.

Overview

In a modular architecture, conflicts are inevitable: two modules might try to define the same page URL or provide the same UI component. JopiJS resolves these conflicts using a Priority System.

In this guide, you will learn:

  • How to use priority files to override code.
  • The different priority levels and their hierarchy.
  • The difference between Replacing and Merging logic.
  • Best practices for setting priorities in your modules.

1. The Core Principle

Whenever JopiJS encounters two elements with the same name (like two pages at /login or two components named Button), it compares their Priority Level.

  • The element with the highest priority wins and is used by the application.
  • The element with the lowest priority is ignored (unless it belongs to a "Merging" category).

Priority Files

Priority is defined by placing a specific empty file inside the element's folder:

src/ mod_my_theme/
└── @alias/
    └── ui/
        └── Button/
            ├── index.tsx
            └── high.priority    # <--- This file sets the priority

2. Priority Hierarchy

JopiJS automatically assigns default.priority if no priority file is present. Here is the hierarchy from highest to lowest:

LevelFile NameTypical Use Case
1 (Highest)veryHigh.priorityEmergency hotfixes or final project overrides.
2high.priorityStandard way for a theme/module to override a core feature.
3default.priorityDefault. Used for most application code.
4low.priorityBase features that are intended to be easily replaced.
5 (Lowest)verylow.priorityDefault "fallback" implementations in third-party modules.

3. Replacing vs. Merging

The priority system behaves differently depending on the type of element:

The "Replace" Behavior

For these elements, the high-priority version completely replaces the lower-priority one.

  • Routes (@routes/)
  • React Components (@alias/ui/)
  • Shared Logic (@alias/lib/ - default behavior)

The "Merge" Behavior

For these elements, JopiJS combines all versions, using priority only to resolve conflicts for specific keys or execution order.

  • Translations (@alias/translations/)
  • Events (@alias/events/)
  • UI Composites (@alias/uiComposites/)

4. Practical Example: Component Override

Imagine mod_core provides a UserAvatar, but you want to use a fancy animated version in your project.

Project Structure:

src/
├── mod_core/
│   └── @alias/ui/UserAvatar/
│       ├── index.tsx
│       └── default.priority     # The standard version
└── mod_fancy_ui/
    └── @alias/ui/UserAvatar/
        ├── index.tsx
        └── high.priority        # The override!

Result: Everywhere in your application where UserAvatar is imported, JopiJS will now serve the version from mod_fancy_ui.


5. The "Very Low" Strategy

Why use verylow.priority? It's the safest way to provide features in a library without breaking the user's existing project.

Scenario: You create a module called mod_user_auth that includes a custom /login page.

If you set your login page to verylow.priority:

  1. If the user already has a /login page (which will have default.priority), their page stays active.
  2. If the user does not have a login page, your page becomes the default.

Designer Tip: Using verylow.priority for your module's default routes and components makes your module "polite"—it only fills in the gaps without forcing its will on the existing project.


6. Seamless Imports

One of the greatest benefits of the priority system is that it is invisible to your business logic.

Even if a component or a translation is overridden by a different module, your import statements remain exactly the same. JopiJS resolves the correct file path behind the scenes based on the highest priority.

src/mod_my_feature/page.tsx
// This import stays the same even if 'Button' is 
// provided by mod_core or overridden by mod_fancy_theme.
import Button from "@/ui/Button";

// Same for translations: JopiJS automatically serves 
// the version with the highest priority or the merged result.
import trProvider from "@/translations/page.login";

export default function MyPage() {
    const tr = trProvider();

    return (
        <div>
            <h1>{tr.welcome_message()}</h1>
            <Button>{tr.click_here()}</Button>
        </div>
    );
}

Development Experience: Since the import path never changes, you don't have to refactor your code when you install a new theme or a module that overrides parts of your site. You can also try changes and revert them in one step by disabling your module (put a _ before his folder name).


Summary

  • Use priority files (high.priority, etc.) to resolve naming conflicts.
  • high.priority is your go-to for overriding third-party components.
  • verylow.priority is recommended for authors providing "fallback" implementations.
  • Remember that Translations and Events are merged, not replaced.