DIY Wedding Website

I recently created our wedding website and figured sharing how I ended up doing it might save someone some time, money and headaches in the future.

I decided to create our wedding website in a “static website” style. A static website is a wesbite with fixed content. IE the content does not update and change with user interactions or remote changes. What’s nice about static sites is there are many providers that will host our website for free. Additionally, we do not need any features from a non-static site, as we’re generally just trying to give information to our wedding guests. The only information we’re looking to receive from our guests is RSVP information, which we’ll cover later in our post.

The first thing you’re going to want to do is register your domain. I’ve personally always registered with namecheap. As they have, as the name implies, incredibly cheap domains. For example. their xyz TLDs can go for as low as $0.88/year, which is what I ended up using.

Now we’ll want to create the content of our website. For this, I decided to use a prebuilt template that I could replace the content with my own. What’s really convenient about this is one can do this for free, as long as they leave the footer attribution. Just to make it absolutely clear, there are many different wedding templates one can find online and many of them have stipulations of free use, with attribution. I spent some time exploring what templates were available out there and ended up liking this one the most.

Once you’ve downloaded your template, just like with any HTML/CSS/JS, we can view the website how it currently exists by opening index.html in our web browser. In simple terms, one can open Firefox, chrome, etc and press CTRL + o and navigate to the index.html that came with your template. That should render what your website will look like.

Now at this point we have to modify the HMTL, CSS and JS of the template to get our website to look like the way we want it. You can go at this two ways:

  1. Open the index.html website in a web browser and right click and select “inspect”. This is “working backwards”, as we’ll look at the visual rendering and find the corresponding HTML/CSS. Once found, modify it as you wish.
  2. Open the index.html in a editor of choice and find the sections you want to modify. Then, once updated, refresh your web browser’s open of the index.html, so you can see the changes.

You’ll basically be modifying the text that renders on the screen, like from “[bride] & [groom’s] wedding” to have your actual names, and modify the dates, addresses, etc. Additionally, you’ll be modifying the images that the template came with.

Once you’re happy with your modifications you should be ready to host the website. I’m a big fan of [https://www.netlify.com/]. They let you link your Github account to a Netlify project, which means that on every Git push to a specific branch, Netlify will redeploy your website. If you haven’t used Github before, I recommend learning about Git and Github by reading through this tutorial. I use Netlify not only for our wedding website, but also this personal blog.

What’s fantastic is they will also handle HTTPS SSL certificates for you. You’ll want to read through their documentation on custom domains to ensure that the domain you purchased from Name Cheap is linked to the hosted website by Netlify.

The last thing we need to be able to handle is to accept user input from our wedding guests, say for RSVP information. Since we’re building out a static website, we don’t have the luxury of having a backend system ready and waiting for our input. Instead, a nice work around is to leverage Google’s APIs and build a Google Sheet that can accept an HTML form’s output as input. This will allow us to put an HTML form on our website and every time a guest submits our form, our Google sheet will be updated and we can receive and email notification. To achieve this I added the following HTML form and corresponding JS script to my wedding website’s index.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<form id="wedding-form">
    <div class="row">
        <div class="col md-6 pb-3">
            <div class="form-group">
                <label for="name-input">Your Name(s)*</label>
                <input class="form-control" id="name-input" type="text" name="name"
                        required="required"/>
            </div>
        </div>
        <div class="col-md-6 pb-3">
            <div class="form-group">
                <label for="email-input">Your Email(s)*</label>
                <input class="form-control" id="email-input" type="email"
                        name="email" required="required"/>
            </div>
        </div>
    </div>
    <!-- <div class="row">
        <div class="col-md-6 pb-3">
            <div class="form-group">
                <label for="code-input">Invite Code*</label>
                <input class="form-control" id="code-input" type="text" name="code"
                        required="required"/>
            </div>
        </div>
    </div> -->
    <div class="row">
        <div class="col-md-6 pb-3">
            <div class="form-group">
                <label for="song-input">Song Request (optional)</label>
                <input class="form-control" id="song-input" type="text" name="song">
            </div>
        </div>
    </div>
    <div class="row">
    <div class="col text-center">
            <button class="btn btn-primary btn-submit" type="submit">Send</button>
        </div>
    </div>
</form>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script>
    $('#wedding-form').on('submit', function (e) {
        //optional validation code here

        e.preventDefault();

        $.ajax({
            url: "https://script.google.com/macros/s/AKfycbwhFra8HB2Ycsjqyc3KjSXWwE1lQcrfhehXPlvQkJNVELBoZ94flM_Hyup2ihozggwg/exec",
            method: "POST",
            dataType: "json",
            data: $("#wedding-form").serialize(),
            success: function (response) {
                console.log("response!");
                console.log($("#wedding-form").serialize());

                if (response.result == "success") {
                    $('#wedding-form')[0].reset();
                    alert('Thank you for contacting us.');
                    return true;
                } else {
                    alert("The form was not submitted successfully. Please let Grehg know and try again later.")
                }
            },
            error: function () {
                alert("The form failed. Please let Grehg know and try again later.")
            }
        })
    });
</script>

Our RSVP form has the following input:

  • “Your Name(s)”
  • Your Email(s)
  • Song Request (optional)

This created our form’s input in the HTML and wired the submission up to our JS script. The JS script serializes the form’s data and submits the data to our “secret” Google Sheet URL.

To get your Google Sheet URL one can create a Google Sheet and than navigate to Tools > script editor. Here we can create a file called Code.gs and insert the following gs script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
function doGet(request){
  return handleRequest(request);
}

function doPost(request){
  return handleRequest(request);
}

//  Enter sheet name where data is to be written below
var SHEET_NAME = "Form Responses 1";

var SHEET_ID = "" // REPLACE THIS WITH YOUR OWN SHEET ID
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service

function handleRequest(request) {
  // shortly after my original solution Google announced the LockService[1]
  // this prevents concurrent access overwritting data
  // [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
  // we want a public lock, one that locks for all invocations
  var lock = LockService.getPublicLock();
  lock.waitLock(30000);  // wait 30 seconds before conceding defeat.

  try {
    // next set where we write the data - you could write to multiple/alternate destinations
    //var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
    var doc = SpreadsheetApp.openById(SHEET_ID);
    var sheet = doc.getSheetByName(SHEET_NAME);

    // we'll assume header is in row 1 but you can override with header_row in GET/POST data
    var headRow = request.parameter.header_row || 1;
    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var nextRow = sheet.getLastRow()+1; // get next row
    var row = [];
    // loop through the header columns
    for (i in headers){
      if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
        row.push(new Date());
      } else if(headers[i] == "sn") {
        row.push(sheet.getLastRow());
      } else { // else use header name to get data
        row.push(request.parameter[headers[i]]);
      }
    }
    // more efficient to set values as [][] array than individually
    sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);

    //send email
    sendEmail(request.parameter);

    // return json success results
    return ContentService
          .createTextOutput(JSON.stringify({"result":"success"}))
          .setMimeType(ContentService.MimeType.JSON);
  } catch(e){
    // if error return this
    return ContentService
          .createTextOutput(JSON.stringify({"result":"error", "error": e.toString()}))
          .setMimeType(ContentService.MimeType.JSON);
  } finally { //release lock
    lock.releaseLock();
  }
}

function setup() {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    SCRIPT_PROP.setProperty("key", doc.getId());
}

function sendEmail(data) {

  var body = '';
  for (let key in data){
   if(data.hasOwnProperty(key)){
     body = body + `<b>${key}</b> : ${data[key]}<br/>`;
   }
  }

  GmailApp.sendEmail('Grehgh@gmail.com', 'Wedding form response', '', {'name': 'Wedding Form', 'htmlBody': 'You have received a new wedding form response. <br/><br/>' + body});
}

You’ll want to replace the SHEET_ID with your own Sheet’s ID, which can be found in the URL of the sheet. For complete transparency, I originally found this script on this page. This will wire up the backend of your Google Sheets API to actually writing records to your Google Sheet.

So there you have it! Using these steps, you can very cheaply create, design and host your very own static wedding website and still receive data from your wedding guests!

If I was concerned with bots filling out our form, we could send an “invite code” out with our wedding invitations and have the front end and back end script inspect the invite code being submitted. Overall, this was a very easy approach to creating our wedding website and ended up costing us under $2.00 USD to host for two years, which was entirely due to the domain name registration.