React‑Router Theme Framework – Vadovas
Šis dokumentas aprašo tavo theme framework griaučius, paremtus react-router, ir parodo, kaip kurti maršrutus, išdėstymus (layouts), sekcijas ir jų „loaderius“. Viskas pailiustruota realiais tavo kodo pavyzdžiais.
1) Projekto esmė ir struktūra
Pagrindinės idėjos
- Tema yra aprašoma faile `` – čia deklaruojami meta duomenys, plėtiniai (extension points), maršrutai, sekcijos, lokalės ir t. t.
- Pagrindinis išdėstymas (layout) –
. Jis naudojabendriems UI elementams ir kontekstams. - Standartinis puslapio apvalkalas (content wrapper) – ``.
- `` – papildomi puslapiai/maršrutai (kai kurie skirti premium vartotojams).
- `` – atskiros kompozicinės sekcijos su savo UI ir (pasirinktinai) serverio loaderiais.
Duomenų srautas trumpai
theme.manifest.jsdeklaruoja routes, extensionPoints ir sections.- Routeris rendrina
layout/theme.tsx→ jis viduje įterpiaWrapperirchildren. - Puslapiai (iš
routes/*) gali turėti loader arba naudoti sekcijų duomenis. - Sekcijos (iš
sections/*) gauna duomenis per loader suts{ request, layout, product, lang } - Komponentuose duomenys pasiekiami su
@pikselis/runtimehooks, pvz.useSectionData("productsSection").
2) theme.manifest.js
Meta
export default {
name: "clothes",
version: "1.0.0",
engine: ">=1.0 <2",
tokens: "tokens.css",
public: "public",
config: { settingsId: "86302a6a-e93a-462d-a03d-3049028e2173" },
locales: "locales",
supportedLanguages: ["lt", "en"],
...
}Paaiškinimai
- name/version/engine – temos identitetas ir suderinamumo langas.
- tokens – kelias į dizaino tokenų CSS (spalvos, tipografija ir pan.).
- public – statinių failų katalogas.
- config.settingsId – išorinė nuostata (pvz., nuotoliniams nustatymams gauti).
- locales/supportedLanguages – i18n resursai ir palaikomos kalbos.
Extension points
extensionPoints: {
layout: "layout/theme.tsx",
header: "sections/Header.tsx",
footer: "sections/Footer.tsx",
page: "layout/Page.tsx"
},- layout – šakninis layout (apgaubia visą aplikaciją).
- header/footer – bendri viršus/apačia, kurie paduodami į
Wrapper. - page – bazinis puslapio „konteineris“ (padding, grid ir pan.).
Maršrutai (routes)
Deklaruojami masyve routes. Minimalus įrašas:
{ path: "/", file: "routes/home.tsx", hasLoader: false }Papildomai:
loader: "loader"– nurodo pavadintą eksportą faile (pvz.,export const loader = ...).- Galima naudoti „wildcards“ ir dinaminius segmentus, pvz.:
/user/orders/*,/prekiu-zenklai/:handle.
Root extensions
rootExtensions: [
{ key: "products", routes: ["^/products/[^/]+$"], file: "rootExtensions/recs.server.ts" }
]Leidžia prijungti server-side logiką tam tikriems keliams (regex), pvz. rekomendacijoms.
Sekcijos (sections)
Sekcijos yra atkartojami UI blokai su konfigūracija:
{
id: "productInfo",
label: "Product Info",
type: "productInfo",
component: "sections/ProductInfo.tsx",
slots: ["product.main"],
propsSchema: {
type: "object",
properties: {
showVendor: { type: "boolean", default: true },
enableTabs: { type: "boolean", default: false }
},
additionalProperties: false
},
blocks: [
{ type: "price", label: "Price", component: "blocks/Price.tsx", maxRepeat: 1 },
{ type: "inventoryBadge", label: "Inventory Badge", component: "blocks/InventoryBadge.tsx", maxRepeat: 3,
propsSchema: { type: "object", properties: { lowThreshold: { type: "integer", default: 5, minimum: 0 } }, additionalProperties: false }
}
],
defaults: { props: { showVendor: true }, blocks: [{ type: "price" }] }
}Svarbu
- id – unikalus identifikatorius (naudojamas
useSectionData(id)). - type – sekcijos tipas (naudojamas loaderių/konfigų logikoje).
- component – React komponento kelias.
- slots – kur sekcija gali būti talpinama (pvz.,
home,category,product,product.main). - propsSchema – JSON schema validuoti nustatymus (props) iš layout konfigūracijos.
- blocks – vidiniai „sub‑blokai“; suteikia smulkesnę kompoziciją.
- defaults – pradinės reikšmės, jei konfigūroje nieko neaprašyta.
3) Layout ir apvalkalai
layout/Page.tsx
Minimalus apvalkalas turiniui:
export default function Page({ children }: React.PropsWithChildren) {
return <section className="section-padding py-8">{children}</section>;
}layout/Wrapper.tsx
Bendras UI apvalkalas, perduodantis Header, Footer, ThemeContext, tvarkantis mobilų overlay ir pan. Svarbūs akcentai:
- `` – tiekiamas
useThemeiš layouto. - Mobilus overlay – reaguoja į
reduxreikšmęui.mobileMenuIsOpeniruseBetterMediaQuery. - Struktūra –
Header→content→Footer.
layout/theme.tsx
Pagrindinis layout su meta tag’ais, guest krepšelio token’u ir „last seen products“:
- `` – grąžina
titleir<link rel="stylesheet" ...>tagus (CSS, fontai, AOS ir pan.). - Guest token –
regenerateCartToken()kviečiamas, jei nėraguestTokenneilocalStorage, neiCookies. - „Last seen products“ – į
Cookiesrašomas sąrašas paskutinių 20 productId (30 d. galiojimas).
Karkasas:
export default function Layout({ header, footer, children, scripts, useTheme }: any) {
// useLocation, useRouteData("root")
// efektai: guestToken, lastSeenProductIds
return (
<Wrapper header={header} footer={footer} useTheme={useTheme}>
{children}
</Wrapper>
);
}4) Maršrutų kūrimas
- Sukurk failą
routes/xyz.tsxsu React komponentu. - Jei reikia duomenų – pridėk pavadintą eksportą
export const loader = async ({ request, params }) => { ... }. - `` įdėk įrašą į
routes:js{ path: "/xyz", file: "routes/xyz.tsx", hasLoader: true, loader: "loader" } - (Pasirinktinai) naudok dinamiką:
:param,*.
Pavyzdžiai iš temos
- Krepšelio žingsniai:
/krepselis,/krepselis/prisijungimas,/krepselis/adresai,/krepselis/mokejimas/*,/krepselis/perziura. - Naudotojo sritis:
/user/info,/user/orders/*,/user/addresses/*,/user/wishlist. - Turinys:
/blog,/blog/:blogSlug,/contacts,/search,/parduotuves.
5) Sekcijų kūrimas ir duomenys
Loaderio parašas
Sekcijos server loader gauna:
// @DOC - passed request, layout, product, lang
const sectionData = await section.loader({
request: args.request,
layout,
product: baseData?.anyPageData?.product,
lang: baseData?.lang
});Loaderio panaudojimas
Komponente:
import { useSectionData } from "@pikselis/runtime";
const data = useSectionData("productsSection");Pavyzdinis loaderis (produktų blokai)
export const loader = (args) => getProductsByType(args);
const getProductsByType = async ({ layout }) => {
const productsBlockSectionData = {};
const tasks = [];
for (const section of layout.sections ?? []) {
if (!['newProducts','discountedProducts','recommendedProducts','productsSection'].includes(section?.type)) continue;
const { type, limit } = section.settings;
let url = '';
switch (type) {
case 'discounted':
url = `/products?filterType=discountedIndexed&selectMerged=1&useIndexedSearch=1&perPage=${limit}`; break;
case 'recommended':
case 'new':
default:
url = `/products?selectMerged=1&useIndexedSearch=1&orderBy=createdAt&orderWay=desc&perPage=${limit}`; break;
}
tasks.push(
getApiGoNoAuth(url).then((res) => {
if (res?.success) productsBlockSectionData[section.id] = res.products;
})
);
}
await Promise.all(tasks);
return productsBlockSectionData;
};Pastabos
- Loaderis vienu kartu gali aptarnauti kelias sekcijas (pagal
layout.sections). - Grąžinama struktūra
sectionId → data, kad kliente būtų galimauseSectionData(id).
6) i18n ir lokalės
- `` – katalogas su vertimais.
- `` – kalbų sąrašas (pvz.,
lt,en). handle.getMetanaudojasettings?.siteTitle[lang]– meta priklauso nuo kalbos.
7) Dizaino tokenai ir vieši failai
- `` – CSS kintamieji, kuriais paremtas temos dizainas.
- `` – paveikslėliai, šriftai, papildomi CSS (
common.css,styles.css,index.css), trečiųjų šalių CSS (pvz., AOS), Google Fonts preconnect ir pan.
8) Praktiniai receptai
A) Nauja sekcija nuo 0
- Sukurk
sections/MySection.tsxsu default export komponentu. - (Nebūtina) pridėk
export const loader = async (args) => { ... }. theme.manifest.jspridėk įsectionsmasyvą:js{ id: "mySection", label: "My Section", type: "mySection", component: "sections/MySection.tsx", slots: ["home"], propsSchema: { type: "object", properties: { title: { type: "string" } } } }- Layouto konfigūroje į atitinkamą slot įtrauk
mySectionsu props. - Komponente gauk duomenis su
useSectionData("mySection").
B) Papildomas maršrutas su loaderiu
routes: [
{ path: "/faq", file: "routes/faq.tsx", hasLoader: true, loader: "loader" }
]Faile routes/faq.tsx:
export const loader = async ({ request }) => {
// fetch from API, cookies, whatever
return { items: [...] };
};
export default function FAQ() {
const data = useLoaderData();
return <Page>{/* render data.items */}</Page>;
}C) Naudotojo sąrašai/detalės su „wildcards“
/user/orders/*, /user/addresses/* leidžia talpinti gilesnius path’us (pvz. /user/orders/123), atskirame faile naudoti params.
9) Gerosios praktikos
- Sekcijų `` stabilumas – nekeisk
id, jei nori išlaikyti sudėliotų blokų ryšį su duomenimis. - `` – griežtas validavimas padeda saugiai redaguoti per UI.
- Loader’iai – agreguok kelias sekcijas viename request’e ten, kur prasminga (kaip
getProductsByType). - SSR/CSR balansas – sekcijų loaderius laikyk server’yje, komponentus – mažus, su memo kur reikia.
- Cache/limitai – API užklausoms naudok limitus (
perPage), pageidautina pridėtiAbortControllerklientui. - Atskirk UI nuo duomenų – komponentai turi skaityti per
useSectionData(id)ir nepažinti backend URL.
10) Greita atmintinė
- Manifestas = vienintelė tiesa apie maršrutus, sekcijas ir extension points.
- Layout (
theme.tsx) tiekia meta, guest krepšelį, last‑seen produktus. - Wrapper – sujungia Header, Footer, kontekstą ir pagrindinę turinio zoną.
- Sekcijos – deklaruojamos manifeste + turi
componentir (pasirinktinai)loader. - Duomenys į sekciją → per
useSectionData("<sectionId>").
11) Naudingi importai (greita nuoroda)
import { useRouteData, useSectionData, useTheme } from "@pikselis/runtime";
import { useRouteLoaderData } from "react-router";
import Cookies from "js-cookie";
import { useSelector } from "react-redux";Klausimai?
Jei reikia, galime pridėti skyrių apie temos konfigūravimą per settings (naudojant settingsId) arba pateikti pilnas sekcijų konfigūravimo JSON pavyzdžius konkretiems slot’ams (pvz., home, category, product).