Skip to content

Templating

@granit/templating is a unified package (no TS/React split) that provides template management — mirroring Granit.Templating and Granit.DocumentGeneration on the .NET backend. It covers CRUD operations, Draft→Published→Archived lifecycle, HTML/PDF/Excel preview rendering, variable discovery, categories, and full revision history.

Built on TanStack Query — all hooks return UseQueryResult or UseMutationResult with automatic cache invalidation.

Peer dependencies: axios, react ^19, @tanstack/react-query ^5

  • Directory@granit/templating/ Types, API functions, provider, hooks (unified package)
PackageRoleDepends on
@granit/templatingDTOs, lifecycle enums, API functions, TemplatingProvider, query/mutation hooksaxios, @tanstack/react-query, react
import { TemplatingProvider } from '@granit/templating';
import { api } from './api-client';
function App({ children }) {
return (
<TemplatingProvider client={api} basePath="/api/v1">
{children}
</TemplatingProvider>
);
}
const TemplateLifecycleStatus = {
Draft: 0,
PendingReview: 1,
Published: 2,
Archived: 3,
} as const;
type TemplateLifecycleStatusValue =
(typeof TemplateLifecycleStatus)[keyof typeof TemplateLifecycleStatus];
const DocumentFormat = {
Html: 0,
Pdf: 1,
Excel: 2,
} as const;
type DocumentFormatValue = (typeof DocumentFormat)[keyof typeof DocumentFormat];
interface TemplateKey {
readonly name: string;
readonly culture?: string;
}
interface TemplateListItem {
readonly name: string;
readonly culture?: string;
readonly category?: string;
readonly status: TemplateLifecycleStatusValue;
readonly mimeType: string;
readonly lastModifiedAt: string;
readonly lastModifiedBy: string;
readonly hasPublishedVersion: boolean;
}
interface TemplateDetail {
readonly name: string;
readonly culture?: string;
readonly category?: string;
readonly draft?: TemplateRevision;
readonly published?: TemplateRevision;
}
interface TemplateRevision {
readonly revisionId: string;
readonly content: string;
readonly mimeType: string;
readonly status: TemplateLifecycleStatusValue;
readonly createdAt: string;
readonly createdBy: string;
readonly publishedAt?: string;
readonly publishedBy?: string;
}
type TemplateRevisionSummary = Omit<TemplateRevision, 'content'> & {
readonly contentLength: number;
};
interface TemplateHistory {
readonly revisions: readonly TemplateRevisionSummary[];
readonly totalCount: number;
readonly page: number;
readonly pageSize: number;
}
interface SaveTemplateRequest {
readonly name: string;
readonly culture?: string;
readonly content: string;
readonly mimeType?: string;
readonly category?: string;
}
interface TemplateListParams {
readonly page?: number;
readonly pageSize?: number;
readonly search?: string;
readonly status?: TemplateLifecycleStatusValue;
readonly category?: string;
readonly culture?: string;
}
interface TemplateLifecycleInfo {
readonly name: string;
readonly culture?: string;
readonly currentStatus: TemplateLifecycleStatusValue;
readonly workflowEnabled: boolean;
readonly availableTransitions: readonly TemplateLifecycleStatusValue[];
}
interface TemplatePreviewRequest {
readonly culture?: string;
readonly format?: DocumentFormatValue;
readonly data?: Record<string, unknown>;
}
interface TemplatePreviewResponse {
readonly html: string;
readonly plainText?: string;
readonly subject?: string;
readonly revisionId: string;
readonly renderTimeMs: number;
}
interface TemplateParseError {
readonly message: string;
readonly line?: number;
readonly column?: number;
readonly snippet?: string;
}
interface TemplateVariable {
readonly name: string;
readonly type: string;
readonly description?: string;
readonly example?: string;
}
interface TemplateVariables {
readonly globalVariables: readonly TemplateVariable[];
readonly modelVariables: readonly TemplateVariable[];
readonly enrichedVariables: readonly TemplateVariable[];
}
interface TemplateCategory {
readonly id: string;
readonly name: string;
readonly description?: string;
readonly icon?: string;
readonly sortOrder: number;
readonly templateCount: number;
}
interface CreateTemplateCategoryRequest {
readonly name: string;
readonly description?: string;
readonly icon?: string;
readonly sortOrder?: number;
}
interface UpdateTemplateCategoryRequest {
readonly name: string;
readonly description?: string;
readonly icon?: string;
readonly sortOrder: number;
}
FunctionEndpointDescription
getTemplates(client, basePath, params?)GET /templatesPaginated template list with filtering
getTemplate(client, basePath, name, culture?)GET /templates/{name}Single template detail
saveDraft(client, basePath, request)POST /templatesCreate a new draft
updateDraft(client, basePath, name, request)PUT /templates/{name}Update an existing draft
deleteDraft(client, basePath, name, culture?)DELETE /templates/{name}Delete a draft
FunctionEndpointDescription
publishTemplate(client, basePath, name, culture?)POST /templates/{name}/publishPublish a template
unpublishTemplate(client, basePath, name, culture?)POST /templates/{name}/unpublishRevert to draft
getLifecycleInfo(client, basePath, name, culture?)GET /templates/{name}/lifecycleCurrent status and available transitions
FunctionEndpointDescription
getHistory(client, basePath, name, params?)GET /templates/{name}/historyPaginated revision history
getRevision(client, basePath, name, revisionId)GET /templates/{name}/revisions/{id}Specific revision content
previewTemplate(client, basePath, name, request)POST /templates/{name}/previewPreview as HTML/text
previewTemplateBinary(client, basePath, name, request)POST /templates/{name}/previewPreview as PDF/Excel (Blob)
getVariables(client, basePath, name)GET /templates/{name}/variablesAvailable template variables
FunctionEndpointDescription
getCategories(client, basePath)GET /template-categoriesList all categories
createCategory(client, basePath, request)POST /template-categoriesCreate a category
updateCategory(client, basePath, id, request)PUT /template-categories/{id}Update a category
deleteCategory(client, basePath, id)DELETE /template-categories/{id}Delete a category
interface TemplatingProviderProps {
client: AxiosInstance;
basePath?: string; // default: '/api/v1'
queryKeyPrefix?: readonly string[]; // default: ['templates']
children: React.ReactNode;
}
<TemplatingProvider client={api}>
{children}
</TemplatingProvider>

Paginated template list with filtering.

function useTemplates(params?: TemplateListParams): UseQueryResult<PaginatedResponse<TemplateListItem>>;

Single template detail. Auto-disables when name is falsy.

function useTemplate(name: string, culture?: string): UseQueryResult<TemplateDetail>;

Available template variables. Cached 5 minutes.

function useTemplateVariables(name: string): UseQueryResult<TemplateVariables>;

Paginated revision history.

function useTemplateHistory(
name: string,
params?: { culture?: string; page?: number; pageSize?: number }
): UseQueryResult<TemplateHistory>;

Specific revision content. Auto-disables when either param is falsy.

function useTemplateRevision(name: string, revisionId: string): UseQueryResult<TemplateRevision>;

All template categories. Cached 5 minutes.

function useTemplateCategories(): UseQueryResult<TemplateCategory[]>;

Grouped mutations for template CRUD and lifecycle. Invalidates related queries on success.

interface UseTemplateMutationsReturn {
readonly saveDraft: UseMutationResult<TemplateDetail, unknown, SaveTemplateRequest>;
readonly updateDraft: UseMutationResult<TemplateDetail, unknown, { name: string; request: SaveTemplateRequest }>;
readonly deleteDraft: UseMutationResult<void, unknown, { name: string; culture?: string }>;
readonly publish: UseMutationResult<void, unknown, { name: string; culture?: string }>;
readonly unpublish: UseMutationResult<void, unknown, { name: string; culture?: string }>;
}

Preview a template as HTML/text.

function useTemplatePreview(): UseMutationResult<
TemplatePreviewResponse, unknown, { name: string; request: TemplatePreviewRequest }
>;

Preview a template as a binary document (PDF or Excel).

function useTemplateBinaryPreview(): UseMutationResult<
Blob, unknown, { name: string; request: TemplatePreviewRequest }
>;

Category CRUD with automatic cache invalidation.

interface UseTemplateCategoryMutationsReturn {
readonly create: UseMutationResult<TemplateCategory, unknown, CreateTemplateCategoryRequest>;
readonly update: UseMutationResult<TemplateCategory, unknown, { id: string; request: UpdateTemplateCategoryRequest }>;
readonly delete: UseMutationResult<void, unknown, string>;
}

For advanced cache control, the package exports a templateKeys factory:

const templateKeys = {
all: (prefix) => [...prefix],
lists: (prefix) => [...prefix, 'list'],
list: (prefix, params) => [...prefix, 'list', params],
details: (prefix) => [...prefix, 'detail'],
detail: (prefix, name) => [...prefix, 'detail', name],
history: (prefix, name) => [...prefix, 'history', name],
revision: (prefix, name, revisionId) => [...prefix, 'revision', name, revisionId],
variables: (prefix, name) => [...prefix, 'variables', name],
categories: (prefix) => [...prefix, 'categories'],
} as const;
graph LR
    Draft["Draft"] --> PendingReview["Pending Review"]
    PendingReview --> Published["Published"]
    Published --> Archived["Archived"]
    PendingReview --> Draft
    Published --> Draft
CategoryKey exportsPackage
EnumsTemplateLifecycleStatus, DocumentFormat@granit/templating
Template typesTemplateDetail, TemplateListItem, TemplateRevision, SaveTemplateRequest@granit/templating
Preview typesTemplatePreviewRequest, TemplatePreviewResponse, TemplateParseError@granit/templating
Variable typesTemplateVariable, TemplateVariables@granit/templating
Category typesTemplateCategory, CreateTemplateCategoryRequest@granit/templating
API functionsgetTemplates(), saveDraft(), publishTemplate(), previewTemplate(), getVariables()@granit/templating
ProviderTemplatingProvider, useTemplatingConfig()@granit/templating
Query hooksuseTemplates(), useTemplate(), useTemplateVariables(), useTemplateHistory(), useTemplateCategories()@granit/templating
Mutation hooksuseTemplateMutations(), useTemplatePreview(), useTemplateBinaryPreview(), useTemplateCategoryMutations()@granit/templating
Cache keystemplateKeys@granit/templating