Chapter 08

Build the button

The moment you have been waiting for. There is not much code, and every line has a reason. First the link between the settings file and the code, then the code itself, one idea at a time.

Pega 24 – 26

Words you will meet

  • React
  • TypeScript
  • withConfiguration

Walkthrough

The code, one idea at a time.

Here is the whole of index.tsx. Step through it: the highlighted lines on the left are explained on the right.

index.tsxTry it: press Next and watch the highlight move
src/components/Acme_UI_CelebrateButton/index.tsx
// ── 1. Imports ──────────────────────────────────────import { useEffect, useState } from 'react';import { Button, withConfiguration } from '@pega/cosmos-react-core';import type { PConnProps } from './PConnProps';import './create-nonce';import StyledAcmeUICelebrateButtonWrapper from './styles'; // ── 2. Helpers & Constants ──────────────────────────// Shown when a designer leaves "Button text" empty, so the button is never blank.const DEFAULT_TEXT = 'Celebrate'; // ── 3. Main Component ───────────────────────────────interface AcmeUICelebrateButtonProps extends PConnProps {  buttonText?: string;  celebrationText?: string;  visibility?: boolean;  disabled?: boolean;} function AcmeUICelebrateButton(props: AcmeUICelebrateButtonProps) {  // 3.1 Props with defaults  const { buttonText, celebrationText = 'Well done!', visibility = true, disabled = false } = props;   // 3.2 State  const [celebrating, setCelebrating] = useState(false);   // Hide the message after a moment. The cleanup stops a timer firing after the button is gone.  useEffect(() => {    if (!celebrating) return undefined;    const timer = setTimeout(() => setCelebrating(false), 2500);    return () => clearTimeout(timer);  }, [celebrating]);   // 3.3 Handlers  const handleClick = () => {    if (disabled) return;    setCelebrating(true);  };   // 3.4 Early return: Pega can ask a component to hide itself.  if (!visibility) return null;   // 3.5 Render  return (    <StyledAcmeUICelebrateButtonWrapper>      <Button variant='primary' disabled={disabled} onClick={handleClick}>        {buttonText?.trim() || DEFAULT_TEXT}      </Button>      {celebrating && <p role='status'>{celebrationText}</p>}    </StyledAcmeUICelebrateButtonWrapper>  );} // ── 4. Exports ──────────────────────────────────────// Default export: wrapped so Pega can feed it its settings at runtime.export default withConfiguration(AcmeUICelebrateButton);// Named export: the bare component, for tests and Storybook, with no Pega around it.export { AcmeUICelebrateButton };

Step 1 of 8

Bring in the tools

Import what the button needs: React’s state and effect helpers, Pega’s own Button and withConfiguration from the Cosmos design system, the type vocabulary, the nonce helper and your styles. Importing Pega’s Button means ours looks and behaves like every other button in Constellation.

Appearance

The looks, in their own file.

Styles live in styles.ts and are written with styled-componentsA library that lets you write CSS inside TypeScript, attached to one component, so styles never leak onto the rest of the page.. The button itself comes from Pega's design system, so it already looks right. All the wrapper adds is a little spacing, and a promise to use whatever font the page uses.

styles.ts
import styled from 'styled-components'; const StyledAcmeUICelebrateButtonWrapper = styled.div`  margin: 0;   /* Use Pega's own font from the page instead of naming one, so the button always matches the app. */  button {    font-family: inherit;  }   p {    margin: 0.5rem 0 0;  }`; export default StyledAcmeUICelebrateButtonWrapper;

Doing it properly

The house standard.

Teams add rules so that any component can be read by anyone. This project's are short: four numbered sections in every index.tsx, named imports from React, comments that explain why rather than what, and two exports. The one detail the walkthrough simplified: in this project the default export also wears an error boundary.

The full export pair
// Default export: wrapped with Pega's runtime plumbing AND an error boundary,// so one broken button cannot blank the whole page.export default withConfiguration(withErrorBoundary(AcmeUICelebrateButton, 'Acme_UI_CelebrateButton')); // Named export: the raw component for tests and Storybook, with no Pega around it.export { AcmeUICelebrateButton };

The component is written. Now to see it, without needing a Pega server at all.

Your turn

Questions and comments

Stuck on something, or have a thought? Write it here. To talk about one part, use the Comment button under that section's heading.

3–20 letters, digits, _ or -. Kept in this browser so you are not asked again.

0/1000

A hands-on guide by Sanjeev Kujur. Independent, and not affiliated with or endorsed by Pegasystems.

Covering Pega Infinity versions 24.2, 25.1, and 26.1 · Updated 5 Nov 2026