Lesson  in  ColdFusion 2025: Foundations

Chart Generation and Management

Generate and customize charts in ColdFusion using cfchart. Visualize dynamic data from the database and integrate charts seamlessly into your application pages.

What is cfchart?

ColdFusion's <cfchart> tag generates charts as PNG images directly on the server — no JavaScript charting library, no client-side rendering, no external dependencies. You point it at a query result and it produces an image that drops straight into a page with a normal <img> tag, or renders inline.

Example bar chart generated by cfchart — title "Open Tickets by Priority", x-axis shows priority values low, medium, high, critical, y-axis shows count 0 to 5, bars are flat blue with no 3D effect, white background, thin grey grid lines

cfchart produces chart images server-side — no JavaScript library needed, the PNG is rendered by the ColdFusion engine.

The two tags you always use together:

TagRole
<cfchart>Defines the chart container — size, format, title, 3D on/off
<cfchartseries>Defines one data series inside the chart — type, query, which columns to use

One <cfchart> can contain multiple <cfchartseries> — useful for grouped or stacked charts.


Chart types

Five small chart preview thumbnails in a horizontal row — bar chart with grouped columns, line chart with a trend line, pie chart with wedge proportions, area chart with a filled region, scatter chart with a dot cloud — each labelled with its cfchartseries type value below

Available cfchartseries types — choose based on whether you are comparing categories, showing trends, or showing proportions.

TypeBest for
barComparing values across categories (e.g. tickets per priority)
lineTrends over time (e.g. tickets created per day)
pieProportions of a whole (e.g. ticket share by status)
areaCumulative or stacked trends
scatterCorrelation between two numeric variables

Key cfchart attributes

<cfchart
  format      = "png"       <!-- png (default), jpg, flash -->
  chartwidth  = "600"       <!-- width in pixels -->
  chartheight = "400"       <!-- height in pixels -->
  title       = "My Chart"  <!-- heading displayed above the chart -->
  show3d      = "false"     <!-- true adds a 3D perspective effect -->
  backgroundColor     = "##ffffff"  <!-- background colour (## escapes # in CFML) -->
  name        = "myChart"   <!-- capture to variable instead of inline output -->
>
Why does the colour attribute use

In CFML, # is the delimiter for expressions — #myVar# outputs the value of myVar. Inside a tag attribute, a literal # sign must be escaped by doubling it: ##. So backgroundColor="##ffffff" means the literal colour #ffffff, not a variable named ffffff. You will see this pattern throughout cfchart colour attributes.


cfchartseries attributes

<cfchartseries
  type        = "bar"         <!-- chart type -->
  query       = "myQuery"     <!-- the cfquery to read data from -->
  itemcolumn  = "priority"    <!-- query column to use for x-axis labels -->
  valuecolumn = "total"       <!-- query column to use for y-axis values -->
  seriescolor = "##4A90D9"    <!-- colour of this series' bars/line/slices -->
  serieslabel = "Tickets"     <!-- legend label for this series -->
>

1. Bar chart — tickets by priority

The most common chart for comparing categories. Query the count of tickets grouped by priority, then feed that directly into a bar series:

<cfquery name="byPriority" datasource="training_db">
  SELECT priority, COUNT(*) AS total
  FROM   hd_tickets
  WHERE  status = 'open'
  GROUP  BY priority
  ORDER  BY total DESC
</cfquery>

<cfchart format="png" chartwidth="600" chartheight="400"
         title="Open Tickets by Priority" show3d="false">
  <cfchartseries type="bar" query="byPriority"
                 itemcolumn="priority" valuecolumn="total"
                 seriescolor="##4A90D9" serieslabel="Tickets">
  </cfchartseries>
</cfchart>

2. Pie chart — tickets by status

Pie charts show proportions. Use the same cfchart wrapper with type="pie" in the series:

<cfquery name="byStatus" datasource="training_db">
  SELECT status, COUNT(*) AS total
  FROM   hd_tickets
  GROUP  BY status
</cfquery>

<cfchart format="png" chartwidth="500" chartheight="400"
         title="Tickets by Status" show3d="false">
  <cfchartseries type="pie" query="byStatus"
                 itemcolumn="status" valuecolumn="total">
  </cfchartseries>
</cfchart>
How cfchart renders — server-side image generation explained

When ColdFusion processes a <cfchart> tag it does not send any chart code to the browser. Instead, the CFML engine calls its internal JFreeChart library (a Java charting engine bundled with ColdFusion since CF8) to render the chart into a bitmap in server memory. The result is a PNG (or JPG) byte array.

What happens by default (inline output): ColdFusion writes the image bytes into a temporary file in its chart cache directory (/opt/coldfusion2025/cfusion/charting/), then emits an <img> tag pointing to that cached file. The browser requests the image as a second HTTP call — you will see this in browser dev tools as a separate request to /CFIDE/charting/....

What happens with name="myChart": The image bytes are captured into a ColdFusion binary variable instead of being written to disk automatically. You then control what happens next — write it to a permanent file with <cffile>, serve it as a download, store it in a database blob, or pass it to another function.

JFreeChart vs modern alternatives: JFreeChart is mature and dependency-free, but produces a raster (pixel) image. For interactive or high-DPI charts in modern web apps, developers often use JavaScript libraries (Chart.js, Highcharts, D3.js) on the client instead. cfchart remains the right choice when you need server-rendered images — PDF reports, email attachments, environments where JavaScript is restricted, or when simplicity matters more than interactivity.

Performance note: Chart generation is CPU-bound. For high-traffic pages that always show the same chart, wrap the cfchart output in a cacheGet/cachePut block (using name="myChart" to capture the binary) so the image is only re-rendered when the underlying data changes.


3. Saving a chart to a file

Use name to capture the chart binary, then write it to disk with <cffile>:

<cfchart format="png" name="ticketChart">
  <cfchartseries type="bar" query="byPriority"
                 itemcolumn="priority" valuecolumn="total">
  </cfchartseries>
</cfchart>

<cffile action="write"
        file="#expandPath('/charts/tickets.png')#"
        output="#ticketChart#">

Serve it with a plain <img> tag: <img src="/charts/tickets.png" alt="Ticket chart">.


Activity 1 — Bar chart from live query data

Getting "The chart package is not installed"? Run these two commands in the Terminal.

ColdFusion 2025 ships the chart engine as an optional package that must be installed separately. This playground has it pre-installed, but if you are running this exercise on your own ColdFusion instance and see this error, fix it with two commands in the Terminal tab:

sudo /opt/coldfusion2025/cfusion/bin/cfpm.sh install chart
sudo /opt/coldfusion2025/cfusion/bin/coldfusion restart

Wait about 30 seconds for ColdFusion to restart, then reload /chart_demo.cfm.

Terminal showing the cfpm.sh install chart command running and completing successfully, followed by the coldfusion restart command — confirming the chart package is installed and ColdFusion has restarted

cfpm.sh install chart installs the chart engine — restart ColdFusion after to load the package.

What you are building: A new file chart_demo.cfm that queries the Help Desk database and renders a bar chart showing open ticket counts by priority.

File to create: /opt/coldfusion2025/cfusion/wwwroot/chart_demo.cfm

In the Terminal tab, run:

sudo tee /opt/coldfusion2025/cfusion/wwwroot/chart_demo.cfm << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>ColdFusion Chart Demo</title>
  <style>
    body    { font-family: sans-serif; max-width: 860px; margin: 2rem auto; }
    h2      { margin-top: 2rem; border-bottom: 1px solid #e5e7eb; padding-bottom: .4rem; }
    .charts { display: flex; flex-wrap: wrap; gap: 2rem; margin-top: 1rem; }
  </style>
</head>
<body>
  <h1>ColdFusion cfchart Demo</h1>

  <h2>Bar Chart — Open Tickets by Priority</h2>

  <cfquery name="byPriority" datasource="training_db">
    SELECT priority, COUNT(*) AS total
    FROM   hd_tickets
    WHERE  status = 'open'
    GROUP  BY priority
    ORDER  BY total DESC
  </cfquery>

  <cfchart format="png" chartwidth="600" chartheight="380"
           title="Open Tickets by Priority" show3d="false"
           backgroundColor="##ffffff">
    <cfchartseries type="bar" query="byPriority"
                   itemcolumn="priority" valuecolumn="total"
                   seriescolor="##3b82d4" serieslabel="Open tickets">
    </cfchartseries>
  </cfchart>

</body>
</html>
EOF

Verify the file exists and contains cfchart:

grep -i "cfchart" /opt/coldfusion2025/cfusion/wwwroot/chart_demo.cfm

Open /chart_demo.cfm in the ColdFusion 2025 browser tab — you should see a blue bar chart with one bar per priority value.

Getting "Table HD_TICKETS not found — this database is empty"? Run the seed script first.
ColdFusion error page showing "Error Executing Database Query — Table HD_TICKETS not found (this database is empty)" with the SQL statement and H2 error code 42104

This error means the Help Desk database has not been seeded yet — the tables exist but contain no data, or the schema was never created.

The database has not been seeded yet. In the ColdFusion 2025 browser tab, go back to the home page and click the DB Test button, then click Run Seed Script. Once the confirmation screen appears, reload /chart_demo.cfm and the chart will render correctly.

Browser showing chart_demo.cfm with a blue bar chart titled "Open Tickets by Priority" — the x-axis shows priority categories and the y-axis shows ticket counts, bars are solid blue on a white background

Bar chart rendered server-side by cfchart — each bar represents the count of open tickets for that priority, pulled live from hd_tickets.


Activity 2 — Add a pie chart for ticket status

What you are building: Extend chart_demo.cfm with a second section showing a pie chart of ticket distribution by status.

File to update: /opt/coldfusion2025/cfusion/wwwroot/chart_demo.cfm

In the Terminal tab, overwrite the file with this extended version:

sudo tee /opt/coldfusion2025/cfusion/wwwroot/chart_demo.cfm << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>ColdFusion Chart Demo</title>
  <style>
    body    { font-family: sans-serif; max-width: 860px; margin: 2rem auto; }
    h2      { margin-top: 2rem; border-bottom: 1px solid #e5e7eb; padding-bottom: .4rem; }
    .charts { display: flex; flex-wrap: wrap; gap: 2rem; margin-top: 1rem; }
  </style>
</head>
<body>
  <h1>ColdFusion cfchart Demo</h1>

  <h2>Bar Chart — Open Tickets by Priority</h2>

  <cfquery name="byPriority" datasource="training_db">
    SELECT priority, COUNT(*) AS total
    FROM   hd_tickets
    WHERE  status = 'open'
    GROUP  BY priority
    ORDER  BY total DESC
  </cfquery>

  <cfchart format="png" chartwidth="600" chartheight="380"
           title="Open Tickets by Priority" show3d="false"
           backgroundColor="##ffffff">
    <cfchartseries type="bar" query="byPriority"
                   itemcolumn="priority" valuecolumn="total"
                   seriescolor="##3b82d4" serieslabel="Open tickets">
    </cfchartseries>
  </cfchart>

  <h2>Pie Chart — All Tickets by Status</h2>

  <cfquery name="byStatus" datasource="training_db">
    SELECT status, COUNT(*) AS total
    FROM   hd_tickets
    GROUP  BY status
    ORDER  BY total DESC
  </cfquery>

  <cfchart format="png" chartwidth="500" chartheight="400"
           title="All Tickets by Status" show3d="false"
           backgroundColor="##ffffff">
    <cfchartseries type="pie" query="byStatus"
                   itemcolumn="status" valuecolumn="total">
    </cfchartseries>
  </cfchart>

</body>
</html>
EOF

Reload /chart_demo.cfm in the browser — you should now see both charts: the bar chart above and the pie chart below it.

Browser showing chart_demo.cfm with both charts visible — the blue bar chart at the top showing open tickets by priority, and a pie chart below it showing all tickets divided into coloured wedges by status with a legend

Both charts on one page — bar chart for priority comparison, pie chart for status proportions — both driven by live hd_tickets data.


Activity 3 — Confirm both charts rendered successfully

What this activity proves:

HTTP 200 only tells you the page loaded — it does not prove the charts actually rendered. The real proof is in the HTML that cfchart emits. When ColdFusion successfully generates a chart image it writes it to its internal chart cache and emits an <img> tag with a src pointing to /CFIDE/charting/cache/.... If that <img> tag is present in the response, both the query ran and the chart image was generated. If the chart failed, ColdFusion throws an exception and no <img> tag appears.

Run this single command in the Terminal tab — it fetches the page and counts how many chart <img> tags are in the response:

curl -s http://localhost:8500/chart_demo.cfm | grep -c "_cf_chart"

Expected result: 2 — one <img> for the bar chart, one for the pie chart. This is the definitive proof that both charts were generated from live query data and written to the CF chart cache successfully.

Terminal showing the curl pipe grep -c "_cf_chart" command returning 2 — confirming two cfchart img tags are present in the response, one for the bar chart and one for the pie chart

grep -c "_cf_chart" returns 2 — both chart images were generated and written to the CF chart cache.

Also confirm the page returns HTTP 200 and contains no errors:

curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8500/chart_demo.cfm
curl -s http://localhost:8500/chart_demo.cfm | grep -i "error\|exception" || echo "No errors found"
Terminal showing the curl command returning HTTP 200 followed by the error grep returning "No errors found" — confirming chart_demo.cfm is accessible and produces no ColdFusion exceptions

HTTP 200 and no errors — the page is accessible and both charts rendered without exceptions.


When all the checks above are green, this lesson is complete. Your progress is saved automatically — move straight on to the next lesson.