Nested Folder Imports#
Jac preserves folder structure during compilation, similar to TypeScript transpilation. This allows you to organize code in nested folders and use relative imports across multiple directory levels.
Folder Structure Preservation#
When Jac compiles your files, it preserves the folder structure in the compiled/ directory:
Source Structure: Compiled Structure:
my-app/ compiled/
├── app.jac → ├── app.js
├── ButtonRoot.jac → ├── ButtonRoot.js
└── level1/ └── level1/
├── ButtonSecondL.jac → ├── ButtonSecondL.js
├── Card.jac → ├── Card.js
└── level2/ └── level2/
└── ButtonThirdL.jac → └── ButtonThirdL.js
Relative Import Syntax#
Jac uses dot notation for relative imports:
- Single dot (
.) - Current directory - Double dot (
..) - Parent directory (one level up) - Triple dot (
...) - Two levels up - Multiple dots - Continue going up the directory tree
- Dot notation after dots - Go down into subdirectories (e.g.,
.level2)
Import Patterns by Directory Level#
Root Level Imports#
From the root directory, you can import from nested folders:
# app.jac (root level)
# Import from root
cl import from .ButtonRoot {
ButtonRoot
}
# Import from level1
cl import from .level1.ButtonSecondL {
ButtonSecondL
}
# Import from level1/level2
cl import from .level1.level2.ButtonThirdL {
ButtonThirdL
}
# Import from level1
cl import from .level1.Card {
Card
}
Second Level Imports#
From level1/, you can import from root (up) or level2 (down):
# level1/ButtonSecondL.jac
# Import from root (go up one level with ..)
cl import from ..ButtonRoot {
ButtonRoot
}
# level1/Card.jac
# Import from root (go up two levels with ..)
cl import from ..ButtonRoot {
ButtonRoot
}
# Import from level2 (go down one level with .level2)
cl import from .level2.ButtonThirdL {
ButtonThirdL
}
Third Level Imports#
From level1/level2/, you can import from root or parent levels:
# level1/level2/ButtonThirdL.jac
# Import from root (go up three levels with ...)
cl import from ...ButtonRoot {
ButtonRoot
}
# Import from second level (go up one level with ..)
cl import from ..ButtonSecondL {
ButtonSecondL
}
Complete Example#
Here's a complete example demonstrating nested folder imports:
Project Structure:
nested-advance/
├── app.jac # Root entry point
├── ButtonRoot.jac # Root level button
└── level1/
├── ButtonSecondL.jac # Second level button
├── Card.jac # Card component (imports from root and level2)
└── level2/
└── ButtonThirdL.jac # Third level button
app.jac:
cl import from .ButtonRoot {
ButtonRoot
}
cl import from .level1.ButtonSecondL {
ButtonSecondL
}
cl import from .level1.level2.ButtonThirdL {
ButtonThirdL
}
cl import from .level1.Card {
Card
}
cl def app() -> any {
return <div>
<ButtonRoot />
<ButtonSecondL />
<ButtonThirdL />
<Card />
</div>;
}
level1/ButtonSecondL.jac:
cl import from ..ButtonRoot {
ButtonRoot
}
cl def ButtonSecondL() -> any {
return <div>
<Button>Second Level</Button>
<ButtonRoot />
</div>;
}
level1/Card.jac:
# Imports from both above (root) and below (level2)
cl import from ..ButtonRoot {
ButtonRoot
}
cl import from .level2.ButtonThirdL {
ButtonThirdL
}
cl def Card() -> any {
return <div>
<ButtonRoot />
<ButtonThirdL />
</div>;
}
level1/level2/ButtonThirdL.jac:
cl import from ...ButtonRoot {
ButtonRoot
}
cl import from ..ButtonSecondL {
ButtonSecondL
}
cl def ButtonThirdL() -> any {
return <div>
<Button>Third Level</Button>
<ButtonRoot />
<ButtonSecondL />
</div>;
}
Import Path Reference#
| Your Location | Target Location | Import Path |
|---|---|---|
app.jac (root) |
ButtonRoot.jac (root) |
.ButtonRoot |
app.jac (root) |
level1/ButtonSecondL.jac |
.level1.ButtonSecondL |
app.jac (root) |
level1/level2/ButtonThirdL.jac |
.level1.level2.ButtonThirdL |
level1/ButtonSecondL.jac |
ButtonRoot.jac (root) |
..ButtonRoot |
level1/Card.jac |
ButtonRoot.jac (root) |
..ButtonRoot |
level1/Card.jac |
level2/ButtonThirdL.jac |
.level2.ButtonThirdL |
level1/level2/ButtonThirdL.jac |
ButtonRoot.jac (root) |
...ButtonRoot |
level1/level2/ButtonThirdL.jac |
level1/ButtonSecondL.jac |
..ButtonSecondL |
Benefits of Nested Folder Structure#
- Relative imports work correctly: Folder structure is preserved, so imports resolve properly
- No file name conflicts: Files with the same name in different folders don't overwrite each other
- Familiar structure: Organize code in nested folders just like in TypeScript/JavaScript projects
- Consistent with modern tooling: Matches the behavior of TypeScript, Babel, and other transpilers
- Scalable organization: Organize large applications into logical folder hierarchies
Best Practices for Nested Folders#
-
Organize by feature or component type: Group related files together
-
Use consistent naming: Keep file names descriptive and consistent
- Limit nesting depth: Avoid going too deep (3-4 levels is usually sufficient)
- Document import patterns: Comment complex import relationships
Common Patterns#
Pattern 1: Component Library Structure#
components/
├── ui/
│ ├── Button.jac
│ └── Input.jac
├── forms/
│ ├── LoginForm.jac
│ └── SignupForm.jac
└── layout/
├── Header.jac
└── Sidebar.jac
Usage:
# From root
cl import from .components.ui.Button { Button }
cl import from .components.forms.LoginForm { LoginForm }
# From components/ui/Button.jac importing from forms
cl import from ..forms.LoginForm { LoginForm }
Pattern 2: Feature-Based Structure#
features/
├── auth/
│ ├── Login.jac
│ └── Signup.jac
├── todos/
│ ├── TodoList.jac
│ └── TodoItem.jac
└── dashboard/
├── Dashboard.jac
└── Stats.jac
Usage:
# From root
cl import from .features.auth.Login { Login }
cl import from .features.todos.TodoList { TodoList }
# From features/auth/Login.jac importing from todos
cl import from ..todos.TodoList { TodoList }
Pattern 3: Mixed Structure#
app.jac # Root entry
components/
├── ui/
│ └── Button.jac
utils/
├── helpers/
│ └── format.jac
hooks/
└── useCounter.jac
Usage:
# From app.jac
cl import from .components.ui.Button { Button }
cl import from .utils.helpers.format { capitalize }
cl import from .hooks.useCounter { useCounter }
# From components/ui/Button.jac
cl import from ...utils.helpers.format { capitalize }
Troubleshooting#
Import Not Found#
- Verify the file exists at the expected path
- Check that the relative path matches the folder structure
- Ensure file extension is
.jac(not.cl.jacfor imports)
Wrong Import Path#
- Count the directory levels between source and target
- Use
..for each level up,.folderfor each level down - Start with
.for current directory imports
File Name Conflicts#
- Use nested folders to avoid conflicts
- Files with the same name in different folders won't conflict
- Folder structure is preserved in compiled output
Examples#
Complete working examples demonstrating nested folder imports:
nested-basic/- Simple nested folder structurenested-advance/- Advanced multi-level nesting
Run any example: