Programming Single Page Applications

Craig Austgen, Brian Capouch, Nathan Samano

Saint Joseph's College

Follow along live:

What, Why, How


Instead of a steady flow of page request/response interaction, there is a one-time transfer of a control program (and its data) from the server to the browser.


The program then takes over the browser window and directs all communication with both the enduser and the backend server.

That program is typically called "the shell"

What is a SPA?

A different way of thinking

  • Browser is a virtual app container
  • Apps are intended to be long-lived
  • URL routing on both client and server


"A SPA isn't like an application; a SPA is an application"

-- Michael Mikowski

Advantages of SPA

  • App-like responsiveness ("fluidity")
  • Less network-latency delay after page load
  • Only one full-page render
  • Offline capabilities
  • Takes advantage of HTML5 features


  • Good ones are intensive, expensive
  • Nav buttons are problematic
  • What's a page view?
  • Analytics, SEO, etc.
  • Memory and namespace management

Frameworks abound


Our sample repo

  • Architecture review
  • Websockets interface
  • SEO concerns

Basic SPA Components

  • Executive (shell)
  • Feature modules render views in regions
  • Aware back-end
  • Model (data)

git clone

cd spa

npm install

node app.js


Notes on repo

  • Simplicity first
  • No data-saving ability
  • Cartoonishly simple
  • Demonstrates basic parts

What is an SPA?

  • Single Page Load
  • Long-lived
  • Like an application
  • Last bullet
<!-- Let's get started -->                                             
    // Calling the toplevel initModule starts up the SPA               
    $(function () { spa.initModule( $('#spa') ); });

<main id="spa"></main>



The SPA takes full ownership of routing requests within its namespace, i.e. it owns all of the URL past the hostname

The shell

The shell is a command and control interpreter, named to suggest its analogy to the well-known OS shell

SPA controls all routing

  • Server-side is done by server package (e.g. Express)
  • #Hash-based client routing is common
  • HTML5 History API pushstate() is the ascendant


// Watch a file and let clients know if/when one changes
setWatch = function ( url_path, file_type ) {
  console.log( 'setWatch called on ' + url_path );
  if ( ! watchMap[ url_path ] ) {
    console.log( 'setting watch on ' + url_path );
    // Part of Node's native 'fs' package
      function ( current, previous ) {
        console.log( 'file accessed' );
        // Send notice 
        if ( current.mtime !== previous.mtime ) {
          console.log( 'file changed: ' + url_path);
          io.sockets.emit( file_type, url_path );
    watchMap[ url_path ] = true;


// Process all traffic looking for "magic names"
app.use( function ( request, response, next ) {
  // Watch for changes to /js/data.js or /css/sockstyle.css file
  if ( request.url.indexOf( '/js/data.js'  ) >= 0 ) {
    setWatch( request.url, 'script' );
  else if ( request.url.indexOf( '/css/sockstyle.css' ) >= 0 ) {
    setWatch( request.url, 'stylesheet' );

  app.use( express.static( __dirname + '/' ) );


var dummyPage = '<b>Name: </b>Javier Flores'
		+ '<br><b> Address: </b>101 Calle Mayor'
		+ '<br><b> Age: </b>52';


+ '<script id="sock_js" src="/js/data.js"></script>'

spa.socket.js initModule()

// Display the value of var "dummyPage" from the data.js file
$(function () {
  jqueryMap.$socketIO.html( dummyPage );

// Set event handler to react to "script" message 
io.connect(serverURL).on('script', function (path) {
  $( '#sock_js' ).remove();
  // Replace contents of element with freshly-loaded value
    '<script id="sock_js"  src="'
      + path +
  // Redisplay HTML with new JavaScript
  jqueryMap.$socketIO.html( dummyPage );

// Set event handler to react to "stylesheet" message
io.connect(serverURL).on('stylesheet', function (path) {
  // Get rid of current style
  $( '#sock_css' ).remove();
  // Replace contents of stylesheet with file from websocket
  // Note which container you append to here is crucial
    '<link id="sock_css" rel="stylesheet" href="'
      + path +
  // Redisplay HTML with new styling
  jqueryMap.$socketIO.html( dummyPage );


cp js/intl/data.js-es js/data.js
cp css/intl/sockstyle.css-es css/sockstyle.css


// Spanish "version" of our SPA view
var dummyPage = '<b>Nombre: </b>Javier Flores'
		+ '<br><b> Dirección: </b>101 Calle Mayor'
		+ '<br><b> Edad: </b>52<br><b> Estado: </b> D.F.';

SEO and SPAs

  • Issue
  • Follow SEO guidelines
  • Our Solution

Traditional Pages

  • Multiple HTML pages
  • Static HTML


  • Single HTML Page
  • Small amount of HTML in Body



curl http://localhost:8000

Traditional crawlers' view

of index.html

curl http://localhost:8000/?_escaped_fragment_=

SPA->Desktop App

Full App w/Electron