use CGI qw /:all -nph/; #on Win32 $|++; sub nest { my $it = shift; ref $it ? complex_table($it) : $it } sub complex_table { my $hr = shift; # could test here to ensure that $hr is indeed a hash ref. table( { -border => 2, -bordercolor => 'green', -cellspacing => '0', }, map Tr( td( {-valign=>'top'}, $_ ), td( nest( $hr->{$_} ))), sort keys %$hr ); } my %h1 = (); # feed your HoH here print start_html, complex_table( \%h1 ), end_html;