Using Prompt Templates in LWC & Apex — Step-by-Step Guide with Code

Learn how to invoke Salesforce Prompt Templates programmatically from Apex and Lightning Web Components to deliver consistent AI-driven experiences across your org.

Introduction

With Agentforce and Einstein 1, Salesforce makes powerful AI available inside Apex, Flows, and Lightning Web Components (LWC). Prompt Templates provide a scalable, centralized way to standardize AI system instructions, tone, context variables, guardrails and expected output formats. Calling Prompt Templates from Apex and LWC unlocks dynamic AI features—summaries, rewrites, chat responses, and more—inside your Salesforce UI.

What are Prompt Templates?

Prompt Templates let admins and developers define reusable AI prompts in Prompt Builder. Instead of duplicating instructions across components, you store the intended behavior and call the template with runtime inputs. Common uses include:

  • System instructions and guardrails
  • Tone and formatting rules
  • Context variables passed from records or UI
  • Consistent output formats (JSON, bullet lists, paragraphs)

Why this pattern matters

Maintaining templates in Prompt Builder centralizes AI behavior, makes testing easier, and ensures consistent outputs regardless of where the template is invoked (Apex, Flow, LWC, or Agentforce Actions).

Use cases

  • Generate article summaries or product descriptions
  • Create chat-style AI responses embedded in LWC
  • Drive intelligent, context-aware form filling
  • Build internal copilots for sales and service

Step 1: Create a Prompt Template in Prompt Builder

In Setup, open Einstein → Prompt Builder and create a new Prompt Template. Example configuration:

  • Name: Adventure Promotion
  • Type: Flex Template
  • Input Variables: pass the Adventure_Master__c record Id or object as needed

Step 2: Invoke a Prompt Template from Apex

Use the ConnectApi EinsteinLLM methods to call a template and pass structured inputs. Below is a sample Apex controller that requests social media posts from the “Adventure_Promotion” template:

public with sharing class SocialMediaPostsController {
    @AuraEnabled
    public static String generateSocialMediaPosts(String adventureActivityId) {
        // Create inputs
        Map<String, String> adventureActivity = new Map<String, String>();
        adventureActivity.put('id', adventureActivityId);
        ConnectApi.WrappedValue adventureActivityValue = new ConnectApi.WrappedValue();
        adventureActivityValue.value = adventureActivity;
        Map<String, ConnectApi.WrappedValue> inputParams = new Map<String, ConnectApi.WrappedValue>();
        inputParams.put('Input:Adventure_Activity', adventureActivityValue);

        // Configure invocation parameters
        ConnectApi.EinsteinPromptTemplateGenerationsInput executeTemplateInput = new ConnectApi.EinsteinPromptTemplateGenerationsInput();
        executeTemplateInput.additionalConfig = new ConnectApi.EinsteinLlmAdditionalConfigInput();
        executeTemplateInput.additionalConfig.applicationName = 'PromptBuilderPreview';
        executeTemplateInput.isPreview = false;
        executeTemplateInput.inputParams = inputParams;

        try {
            // Call the service
            ConnectApi.EinsteinPromptTemplateGenerationsRepresentation generationsOutput = ConnectApi.EinsteinLLM.generateMessagesForPromptTemplate(
                'Adventure_Promotion',
                executeTemplateInput
            );
            ConnectApi.EinsteinLLMGenerationItemOutput response = generationsOutput.generations[0];
            return response.text;
        } catch (Exception e) {
            System.debug(e.getMessage());
            throw e;
        }
    }
}

Step 3: Call Apex from a Lightning Web Component

From LWC, call the Apex method and render the AI output in the UI. The LWC below triggers the Apex controller and displays LinkedIn/Twitter/Slack variations. It attempts to parse JSON returned from the template and falls back to a single text response if parsing fails.

<template>
    <lightning-card title="Adventure Social Media Posts" icon-name="utility:socialshare">
        <div class="slds-p-around_small">
            <lightning-button
                variant="brand"
                label="Generate Social Media Post"
                title="Generate Social Media Post"
                onclick={handleGeneratePosts}
                class="slds-m-bottom_small"
            ></lightning-button>

            <template if:true={showSpinner}>
                <div class="slds-is-relative slds-m-vertical_medium">
                    <lightning-spinner alternative-text="Generating posts..." size="medium"></lightning-spinner>
                </div>
            </template>

            <template if:true={error}>
                <div class="slds-text-color_error slds-m-bottom_medium">
                    Error: {error.message}
                </div>
            </template>

            <template if:false={showSpinner}>
                <div class="slds-grid slds-gutters slds-wrap">
                    <div class="slds-col slds-size_1-of-1 slds-p-around_small">
                        <lightning-textarea
                            label="LinkedIn Post"
                            value={linkedinPost}
                            readonly
                            rows="10"
                            class="post-textarea"
                        >
                        </lightning-textarea>
                    </div>
                    <div class="slds-col slds-size_1-of-1 slds-p-around_small">
                        <lightning-textarea
                            label="Twitter Post"
                            value={twitterPost}
                            readonly
                            rows="10"
                            class="post-textarea"
                        >
                        </lightning-textarea>
                    </div>
                    <div class="slds-col slds-size_1-of-1 slds-p-around_small">
                        <lightning-textarea
                            label="Slack Post"
                            value={slackPost}
                            readonly
                            rows="10"
                            class="post-textarea"
                        >
                        </lightning-textarea>
                    </div>
                </div>
            </template>
        </div>
    </lightning-card>
</template>
import { LightningElement, api } from 'lwc';
import generateSocialMediaPosts from '@salesforce/apex/SocialMediaPostsController.generateSocialMediaPosts';

export default class AdventureSocialLWC extends LightningElement {
    @api recordId;
    
    linkedinPost = '';
    twitterPost = '';
    slackPost = '';
    error;
    showSpinner = false;

    async handleGeneratePosts() {
        this.showSpinner = true;
        this.error = undefined;
        this.linkedinPost = '';
        this.twitterPost = '';
        this.slackPost = '';

        try {
            const posts = await generateSocialMediaPosts({
                adventureActivityId: this.recordId
            });
            
            // Try to parse as JSON first
            try {
                const parsedPosts = JSON.parse(posts);
                this.linkedinPost = parsedPosts.linkedin || '';
                this.twitterPost = parsedPosts.twitter || '';
                this.slackPost = parsedPosts.slack || '';
            } catch (parseError) {
                // If parsing fails, treat the entire response as a single post
                // and distribute it among the platforms (this is a fallback)
                this.linkedinPost = posts;
                this.twitterPost = posts;
                this.slackPost = posts;
            }
        } catch (error) {
            this.error = error;
            console.error('Error generating social media posts:', JSON.stringify(error));
        } finally {
            this.showSpinner = false;
        }
    }
}

Best practices

  • Design prompt templates with clear system instructions and expected output shapes (e.g., JSON) so Apex/LWC can parse reliably.
  • Use preview and testing modes in Prompt Builder before enabling production calls.
  • Handle timeouts, exceptions, and malformed responses gracefully in Apex and LWC.
  • Control costs by caching or limiting calls for non-critical use cases.

Conclusion

Invoking Prompt Templates directly from Apex and LWC is a practical pattern to embed controlled, consistent AI inside Salesforce applications. It enables knowledge assistants, dynamic form builders, internal copilots, and more—while keeping AI behavior centralized and maintainable.

Why this matters for Salesforce teams: admins can manage prompt behavior centrally in Prompt Builder, developers can integrate AI with familiar Apex and LWC patterns, and business users get predictable, useful AI outputs that improve productivity.