File Conventions

Catalyst keeps the application structure explicit. Routes, server hooks, client entry points, and static assets each have clear locations so the framework can build, render, and hydrate the app predictably.

The App Contract

Catalyst imports a fixed set of files from your app by path. These paths are not configurable. If a file is missing, or does not export what Catalyst expects, Catalyst reports it at startup with an error naming the file and the missing export.

PathExpected exportUsed for
server/document.jsdefault export: React componentThe outer HTML document
server/index.js (optional)named exports: preServerInit and the SSR lifecycle hooksServer lifecycle
server/server.jsnamed export: addMiddlewares functionExpress middleware registration
src/js/containers/App/indexdefault export: React componentApplication shell
src/js/routes/utilsnamed export: getRoutes functionRoute configuration
src/js/store/index.js (optional)default export: configureStore functionRedux store creation

server/index.js is optional. Every importer wraps the import in a try/catch and continues, so a missing file simply means the app registers no server lifecycle hooks. Every hook the file exports is itself optional too.

src/js/store/index.js is optional. When the file is absent, a Vite plugin (optionalTemplateStorePlugin) substitutes a stub store exposing getState, dispatch, subscribe, and replaceReducer. The none-js and none-ts templates ship without a store file and rely on this stub.

The remaining four paths are mandatory.

Required Module Aliases

Your root package.json must define a _moduleAliases object containing all six aliases below. Catalyst resolves app code through them during build and SSR.

AliasConventional target
@apiapi.js
@containerssrc/js/containers
@serverserver
@configconfig
@csssrc/static/css
@routessrc/js/routes/

Alias names may not start with @catalyst; that prefix is reserved for the framework's own internal aliases.

Startup Validation

Catalyst checks part of the contract at load time and reports every violation it finds through the shared error reporter, then continues starting. Each report names the specific file and export involved along with what the app must provide, and its detail level follows the output mode (see the error handling guide). A violation is not fatal on its own: the app keeps booting and the real failure surfaces where the missing piece is used, so read the startup output when a render fails for no obvious reason.

Three checks are wired:

  • getRoutes and configureStore are validated when the renderer module loads — at server startup in production, and on the first SSR request in development, where the handler is loaded lazily.
  • The addMiddlewares export from server/server.js is validated for shape during server setup.
  • A missing server/server.js does fail at startup outright: its import is unguarded, unlike the try/catch around server/index.js.

catalyst start, build and serve also check config/config.json and package.json before doing anything else, and unlike the checks above these are fatal. The command exits with a coded error if either file is missing or cannot be parsed, if config.json lacks any of NODE_SERVER_HOSTNAME, NODE_SERVER_PORT, BUILD_OUTPUT_PATH, PUBLIC_STATIC_ASSET_PATH, PUBLIC_STATIC_ASSET_URL or CLIENT_ENV_VARIABLES, or if the module aliases above are missing one of the six or define a name starting with @catalyst. Apps created from the templates already have all of them. See Configuration for the full key list.

Core Project Layout

PathPurpose
config/config.jsonRuntime and build configuration
src/js/routes/index.jsRoute definitions
src/js/routes/utils.jsRoute preparation and RouterDataProvider wiring
client/index.jsClient entry and hydration
server/index.jsServer lifecycle hooks
server/server.jsExpress middleware registration
server/document.jsHTML document template
public/Static files served as-is
src/static/Source-controlled CSS, fonts, and other bundled static assets

Routing Files

Catalyst uses a central route definition file instead of file-based routing.

import HomePage from "@pages/HomePage";
import ProductPage from "@pages/ProductPage";
const routes = [
{
path: "/",
element: <HomePage />,
},
{
path: "/product/:id",
element: <ProductPage />,
},
];
export default routes;

Common route properties:

PropertyPurpose
pathURL pattern
elementReact element to render
childrenNested routes
data or route fetchersRoute-level data loading, depending on the app setup
preloadOptional preloading behavior

Route Components

Page components typically live under your pages or containers directory and can define route fetchers on the component itself:

function ProductPage() {
return <div>...</div>;
}
ProductPage.serverFetcher = async ({ params }) => {
return fetchProduct(params.id);
};
ProductPage.clientFetcher = async ({ params }) => {
return fetchProduct(params.id);
};
export default ProductPage;

Layout and Shell

Persistent UI such as headers, footers, and navigation belongs in the app shell rather than in every page component. The shell stays mounted while matched route components change inside it.

Typical responsibilities:

  • global providers
  • top-level layout structure
  • navigation
  • route outlet placement

Server Files

server/index.js

Export lifecycle hooks used by the SSR server:

  • preServerInit
  • onRouteMatch
  • onServerError

An empty export is valid if the app does not need custom server hooks yet.

server/server.js

Register Express middlewares, request handling extensions, and any app-specific server customisation.

server/document.js

Controls the outer HTML document structure, including tags, metadata wrappers, and shell-level markup.

Static Asset Conventions

LocationUsage
public/Files served without bundling
src/static/cssGlobal stylesheets
src/static/fontsFont files
public/android/Android-specific native assets
public/ios/iOS-specific native assets

For universal apps, keep platform assets under public/android and public/ios so the native build scripts can discover them during packaging.

  • Keep route definitions in src/js/routes/index.js.
  • Keep route preparation logic in src/js/routes/utils.js.
  • Keep reusable layout code in a dedicated app shell container.
  • Treat config/config.json as the single source of truth for runtime configuration.