fx Macro Recorder & VBA Basics

Range, Cells, Offset, .End(xlUp) — finding the last row

⏱ 13 min

What you'll learn

  • Range — by address
  • Cells — by row and column number
  • Offset — move from a cell

Concept

1. Range — by address

ws.Range("A1").Value = "Name"
ws.Range("A1:E1").Font.Bold = True
ws.Range("A:A").ColumnWidth = 20

Good when the address is fixed.

2. Cells — by row and column number

ws.Cells(2, 1).Value          ' row 2, column 1 = A2
ws.Cells(2, "B").Value        ' column letter works too

Perfect inside loops, because row and column can be variables:

ws.Cells(i, 3).Value = ws.Cells(i, 2).Value * 1.18

Combine both to build a range:

ws.Range(ws.Cells(2, 1), ws.Cells(lastRow, 5))   ' A2 to E(lastRow)

3. Offset — move from a cell

ws.Range("A1").Offset(1, 2)   ' 1 row down, 2 columns right = C2

Negative numbers move up or left.

4. Resize — change the size

ws.Range("A2").Resize(10, 5)  ' 10 rows × 5 columns starting at A2 = A2:E11

5. Finding the last row — .End(xlUp)

Data grows every day, so never hard-code A2:A500. Use:

Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

How it works: start at the very bottom cell of column A (row 1,048,576), then do what Ctrl + ↑ does — jump up to the last filled cell. .Row gives its row number.

Pick a column that is always filled (like Date or Invoice No). A column with blanks at the end gives a smaller number.

Next empty row for adding a record:

nextRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1

Last column in row 1:

lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column

6. Why not .End(xlDown)?

Range("A1").End(xlDown) stops at the first blank cell. One empty row in the middle and your macro misses everything below it. If column A is completely empty below A1, it jumps to row 1,048,576. xlUp from the bottom avoids both problems.

7. CurrentRegion and UsedRange

ws.Range("A1").CurrentRegion   ' block around A1 until a fully empty row/column
ws.UsedRange                   ' everything Excel thinks was ever used

CurrentRegion is handy for a clean table. UsedRange can include old formatted cells, so don't rely on it for the last row.

8. Example: add a new entry at the bottom

Sub AddEntry()
    Dim ws As Worksheet, nextRow As Long
    Set ws = ThisWorkbook.Worksheets("Data")

    nextRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1
    ws.Cells(nextRow, 1).Value = Date
    ws.Cells(nextRow, 2).Value = "Rahul Sharma"
    ws.Cells(nextRow, 3).Value = 25000
End Sub

Common mistakes

Hard-coding the last row. Using a column with gaps for .End(xlUp). Using Rows.Count without the sheet (ws.Rows.Count) when working across sheets. Mixing up Cells(row, column) order.

Exercises

mediumOn a sheet with data in A1:E?, write a macro that shows (with Debug.Print) the last row, the last column, and the address of the data range built with Range(Cells(...), Cells(...)). Add 3 rows of data and run again to confirm it adapts.
Within With ThisWorkbook.Worksheets("Data"), use lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row and lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column. Print .Range(.Cells(1, 1), .Cells(lastRow, lastCol)).Address. The starter Data sheet ends at row 31, column 5; after three populated rows it ends at row 34. Column A must be populated.

Quiz

Cells(3, 4) is which cell?
D3
Range("B2").Offset(-1, 1)?
C1
Why .End(xlUp) from the bottom instead of .End(xlDown)?
Blank cells in between don't break it
Range, Cells, Offset, .End(xlUp) — finding the last row · Automation | ExcelWalaa